1. 集合的概念
集合是一種數(shù)學概念,表述的是“由一個或多個確定的元素所構成的整體”。
1.1 Java中集合與數(shù)組的比較
數(shù)組
- 數(shù)組在初始化時是定長的,初始化后數(shù)組長度不可改變。(長度表示的是最大容量)
- 數(shù)組中只能存放同類型的數(shù)據(jù),比如int數(shù)組只能存數(shù)字不能存String
- 數(shù)組中沒有現(xiàn)成API方法使用,數(shù)組只有一個length屬性可用
集合
- 集合在初始化時可以不設置長度,隨著元素放入集合,長度可變。(集合中已經存放了多少個元素)
- 集合在沒有約束泛型的情況下,其可以存放任意類型的數(shù)據(jù)
- 集合類中提供了大量的API方法,方便程序開發(fā)者操作集合
1.2 集合的分類

- Collection接口(線性集合)
(1) List接口(有序號可重復)
ArrayList實現(xiàn)類基于數(shù)組方式實現(xiàn)
LinkedList實現(xiàn)類基于鏈表方式實現(xiàn)
(2) Set接口(無序號不重復)
HashSet實現(xiàn)類基于Hash表算法 - Map接口(鍵值集合)
HashMap實現(xiàn)類基于Hash表算法 - Iterator接口(迭代器)
2. 線性集合(Collection接口)
2.1 線性集合的概念
Collection接口表述的是“線性集合”,線性結構是n個數(shù)據(jù)元素的有序(次序)集合
線性集合的特征
1.集合中必存在唯一的一個"第一個元素"。
2.集合中必存在唯一的一個"最后的元素"。
3.除最后元素之外,其它數(shù)據(jù)元素均有唯一的"后繼"。
4.除第一元素之外,其它數(shù)據(jù)元素均有唯一的"前驅"。
2.2 線性集合(Collection)的實現(xiàn)類
2.2.1 List接口
有序集合(也稱為序列 )。 該界面的用戶可以精確控制列表中每個元素的插入位置。 用戶可以通過整數(shù)索引(下標)訪問元素,并搜索列表中的元素。允許集合中存放重復的數(shù)據(jù)。
有序號可重復
2.2.1.1 ArrayList實現(xiàn)類
底層是基于數(shù)組實現(xiàn)的,方法都是線程不安全的
API方法
- add(Object o)
返回值是boolean
在集合的結尾處添加一個元素 - add(int index, Object o)
返回值是void
在index位置插入一個元素,插入后,后面的元素會自動后移 - addAll(Collection c)
返回值是boolean
在集合結尾處添加集合c中的所有元素 - clear()
返回值是void
清空集合,保留集合對象 - contains(Object o)
返回值是boolean
在集合中查找o是否存在,存在判斷依據(jù)是元素是否“equals”o
如果在集合中找到了“相等”的元素,返回true,否則返回false - get(int index)
返回值是Object
獲得集合中index位置上的元素,不會移除這個元素 - set(int index, Object o)
返回值是Object
用指定元素o替換集合中index位置上的元素,并返回被替代的元素 - remove(int index)
返回值是Object
移除集合中index位置上的元素,并返回這個元素 - remove(Object o)
返回值是boolean
移除集合中的第一個o元素(判斷依據(jù)是集合中的某個元素“equals”o),返回是否移除成功。 - size()
返回值是int
返回集合中元素的個數(shù)(集合的長度) - indexOf(Object o)
返回值是Object
查找o在集合中位置,如果沒找到返回-1
public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList();
System.out.println(list.size());
list.add(new String("哈哈"));
list.add(23);
list.add(new Date());
list.add("哈哈");
list.add(3.14);
System.out.println(list.size());
System.out.println("插入了5個元素==========================");
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("在2位置插入了嘻嘻==========================");
list.add(2, "嘻嘻");
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("替換2位置為嘿嘿==========================");
list.set(2, "嘿嘿");
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("移除位置2的元素==========================");
list.remove(2);
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("移除元素“3.14”的元素==========================");
list.remove(3.14);
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("移除元素“哈哈”的元素==========================");
list.remove(new String("哈哈"));
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("判斷集合中是否有“哈哈”的元素==========================");
boolean b = list.contains("哈哈");
System.out.println(b);
System.out.println("判斷集合中是否有“3.14”的元素==========================");
boolean x = list.contains(3.14);
System.out.println(x);
System.out.println("清空集合==========================");
list.clear();
System.out.println(list.size());
}
}
如果遇到了集合中有元素2,且還需要移除2下標時,注意寫法
list.remove(2); //移除下標位置2的元素
list.remove(new Integer(2)); //移除集合中元素2
2.2.1.2 LinkedList實現(xiàn)類
底層是基于鏈表實現(xiàn)的
主要的增刪改查的方法參照ArrayList類的API學習
特有API方法
- addFirst(Object o)和addLast(Object o)
返回值void
在集合的開始和結尾處插入一個元素o - getFirst()和getLast()
返回值Object
返回集合的開始和結尾處的元素
將LinkedList看做為一個棧
- peek()
返回值Object
查看棧頂元素,但不移除它 - poll()和pop()
返回值Object
出棧 - push(Object o)
返回值void
將Object o入棧
public class Test3 {
public static void main(String[] args) {
LinkedList<Object> list = new LinkedList<Object>();
list.push("哈哈");
list.push("嘻嘻");
list.push(100);
list.push(3.14);
list.push(new Date());
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("查看棧頂==============================");
Object x = list.peek(); //查看棧頂
System.out.println(x);
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("出棧==============================");
Object y = list.pop();
System.out.println(y);
System.out.println("====");
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("入棧==============================");
list.push("abc");
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
System.out.println("出棧==============================");
Object z = list.poll();
System.out.println(z);
System.out.println("====");
for(int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
}
}
}
2.2.1.3 Vector實現(xiàn)類
底層是基于數(shù)組實現(xiàn)的,方法都是線程安全的
API方法參照ArrayList學習
筆試題/面試題
(1)ArrayList和LinkedList的區(qū)別?
本質上就是問“數(shù)組”與“鏈表”的區(qū)別。
ArrayList基于數(shù)組實現(xiàn)的,LinkedList基于鏈表實現(xiàn)
數(shù)組:在查詢元素速度快,插入刪除速度慢
鏈表:在查詢元素速度慢,插入刪除速度快
(2)ArrayList和Vector的區(qū)別?
ArrayList線程不安全,Vector線程安全
2.2.2 Set接口
不包含重復元素的集合。如果有元素e1和e2(e1.equals(e2) 為true),e1和e2只能保留一個元素。Set接口的特征是使用者無法控制元素的存放順序,其存放順序有Set接口的實現(xiàn)類自身的算法決定
2.2.2.1 HashSet實現(xiàn)類
實現(xiàn)算法是基于Hash表算法,存放順序不由使用者控制,允許存放一個null值
Object類中有hashcode方法,返回int值,該int值是由對象的內存地址計算出來的
一個對象會有一個HashCode(哈希碼,Hash碼,HashCode),它有如下特征:
- 如果兩個對象擁有相同的內存地址,他們一定有相同的哈希碼
-
如果兩個對象擁有相同的哈希碼,他們的內存地址可能不一樣
哈希表算法,按照哈希碼的大小順序存放數(shù)據(jù)。
比如:字符串“哈哈”的哈希碼是34,字符串“嘻嘻”的哈希碼是20。
使用代碼存入集合的順序:哈哈,嘻嘻
實際集合中存儲順序:嘻嘻,哈哈
比如:字符串“哈哈”的哈希碼是34,字符串“嘻嘻”的哈希碼是20,字符串“嘿嘿”的哈希碼也是34。
使用代碼存入集合的順序:哈哈,嘻嘻
實際集合中存儲順序:嘻嘻,(哈哈,嘿嘿)
類似的存儲結構
API方法
- add(Object o)
返回值boolean
將o對象放入集合,放入的位置由集合的算法自身決定 - clear()
返回值void
清空集合 - isEmpty()
返回值boolean
判斷集合是否為空 - remove(Object o)
返回值boolean
在集合中移除o元素 - size()
返回值int
返回集合的長度 - iterator()
返回值Iterator
返回set集合對應的迭代器
因為Set集合無法使用下標,所以不能通過下標進行遍歷訪問集合中的元素。只能通過迭代器去遍歷Set集合
2.2.3 Iterator(迭代器)
負責通過一個向前的“游標”不斷的訪問元素和移動,完成Set集合的遍歷。

API方法
- hasNext()
返回值:boolean
判斷游標當前位置之后是否有數(shù)據(jù) - next()
返回值:Object
取得游標當前位置之后的數(shù)據(jù),再將游標向后移動一位
由于next()包含了移動的操作,所以在循環(huán)中應該只調用一次
示例:利用迭代器遍歷set集合
public class Test1 {
public static void main(String[] args) {
HashSet<Object> set = new HashSet<Object>();
set.add("哈哈");
set.add(3.14);
set.add(new Date());
set.add("hehe");
set.add(23);
set.add(12);
System.out.println(set.size());
Iterator<Object> ite = set.iterator();//獲得了set集合對應的迭代器
while(ite.hasNext()) {
Object o = ite.next();
System.out.println(o);
}
}
}
面試題/筆試題:
(1)List集合和Set集合的區(qū)別?
List集合:有序可重復,可以通過下標訪問數(shù)據(jù),存放順序可控,允許存放重復數(shù)據(jù)
Set集合:無序不重復,不能通過下標訪問數(shù)據(jù),存放數(shù)據(jù)由實現(xiàn)類自己決定,重復數(shù)據(jù)只能保留一個
(2)HashSet和TreeSet的區(qū)別?
HashSet基于哈希表算法實現(xiàn)
TreeSet基于紅黑樹算法實現(xiàn)
很多時候使用Set集合完成去重復的操作。
3. 鍵值集合(Map接口)
Map集合是一種以鍵值對為單位,進行數(shù)據(jù)存儲的一種結構,它與線性集合有所區(qū)別:線性集合一個位置上只存儲一個數(shù)據(jù),鍵值對集合一個位置上存儲的是一個鍵(key)和一個值(value)。不同數(shù)據(jù)之間鍵(key)是不能重復的,值(value)是也可重復的。存放數(shù)據(jù)時,必須提供key和value,取得數(shù)據(jù)時一般是根據(jù)key來獲取value
Map的主要實現(xiàn)類
3.1 HashMap和HashTable
基于哈希表算法實現(xiàn)存儲
3.1.1 構造方法
- HashMap()
創(chuàng)建一個初始容量是16,負載因子是0.75的默認的對象
初始容量:對象初始默認可以存放16個鍵值對
負載因子:表示當容量達到最大容量的75%時(比如,初始16,16的75%是12),,容量會加倍擴容(16*2=32)。
這個構造方法是比較常用的 - HashMap(int init)
創(chuàng)建一個初始容量是init,負載因子是0.75的對象 - HashMap(int init, float ext)
創(chuàng)建一個初始容量是init,負載因子是ext的對象 - HashMap(Map map)
利用map的數(shù)據(jù)重新構建一個同樣數(shù)據(jù)的HashMap
3.1.2 API方法
- put(Object key, Object value)
返回值:Object
將key-value組成的鍵值對,放入集合;如果集合中已經存在了同key的鍵值對,會利用新的key-value替換原有的key-value
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(101, "趙四"); //向集合中添加一條數(shù)據(jù)
map.put(102, "劉能"); //向集合中添加一條數(shù)據(jù)
map.put(101, "哈哈"); //替換集合中原來101這條數(shù)據(jù)
- remove(Object key)
返回值:Object
根據(jù)key值,刪除集合中的一個鍵值對,并返回對應value
這個方法是使用頻率較高的
map.remove(103);
- remove(Object key, Object value)
返回值:boolean
根據(jù)key-value值,刪除集合中的一個鍵值對。返回是否刪除成功
map.remove(104,"劉能"); //成功,有這對數(shù)據(jù)
map.remove(102,"aaa");//失敗,雖然有102,102中不是aaa
- clear()
返回值:void
清空整個集合
map.clear();
- get(Object key)
返回值:Object
根據(jù)key值,返回集合中key對應value,如果集合中沒有這個key,返回null
String v1 = map.get(103);
String v2 = map.get(105);
- size()
返回值:int
返回集合中鍵值對的個數(shù)
System.out.println("集合中的元素個數(shù)是:"+map.size());
- entrySet()
返回值:Set<Entry>
一個entry對象表示一個鍵值對
返回所有鍵值對組成的一個Set集合
Set<Entry<Integer, String>> set = map.entrySet();//得到鍵值對的Set集合
Iterator<Entry<Integer, String>> ite = set.iterator();//將set集合變成迭代器
while(ite.hasNext()) { //遍歷迭代器
Entry<Integer, String> e = ite.next(); //一個鍵值對
System.out.println(e.getKey()+":"+e.getValue());
}
- keySet()
返回值:Set<Object>
返回所有key值組成的一個Set集合
Set<Integer> kset = map.keySet(); //所有key值組成的set集合
Iterator<Integer> kite = kset.iterator(); //將set集合變成迭代器
while(kite.hasNext()) { //遍歷迭代器
int key = kite.next();
String value = map.get(key); //取得value
System.out.println(key+":"+value);
}
- isEmpty()
返回值:boolean
判斷集合是否為空集合
boolean x = map.isEmpty();
- containsKey(Object key)
返回值boolean
判斷集合中的key是否包含參數(shù)key,(判斷依據(jù):equals)
boolean b1 = map.containsKey(101);
boolean b2 = map.containsKey(105);
- containsValue(Object value)
返回值boolean
判斷集合中的value是否包含參數(shù)value,(判斷依據(jù):equals)
boolean b3 = map.containsValue("劉能");
boolean b4 = map.containsValue("AAA");
面試題/筆試題
(1)HashMap與HashTable的區(qū)別
HashMap是線程不安全的,HashTable是線程安全。
HashMap中允許null作為key,HashTable不允許null作為key
(2)HashMap與TreeMap的區(qū)別
HashMap底層基于Hash表算法
TreeMap底層基于二叉樹算法
(3)HashMap的桶容量(擴容機制)
以默認hashmap()構造方法解釋:,默認初始值16,負載因子0.75.
初始時hashmap的最大容量是16,當存儲數(shù)據(jù)至12時(16*0.75),進行擴容,擴容方式是16*2=32; 當繼續(xù)存儲數(shù)據(jù)至24時(32*0.75),繼續(xù)擴容,擴容方式32*2=64
3.2 TreeMap
基于紅黑樹算法實現(xiàn)存儲
參照HashMap的API去理解
- put
- get
- remove
- keySet
- entrySet
- containsKey
4. 泛型
泛型是用于約束集合中輸入數(shù)據(jù)類型的。集合默認是可以將任何數(shù)據(jù)都放入其中,很多情況下我們需要約束集合中輸入的元素類型,借助于泛型可以達到效果。
泛型的優(yōu)勢
- 約束輸入至集合的數(shù)據(jù)類型,控制數(shù)據(jù)準確
- 從集合輸出數(shù)據(jù)時,無需考慮類型轉換問題
- 可以使用for...each句式快速遍歷集合
約束輸入類型為String
ArrayList<String> list = new ArrayList<String>();
由于泛型約束只支持引用數(shù)據(jù)類型,如果需要約束一個整數(shù)集合,需要使用包裝類
ArrayList<Integer> list = new ArrayList<Integer>();
泛型約束Employee類的集合
ArrayList<Employee> list = new ArrayList<Employee>();
泛型約束其他的集合
ArrayList<HashSet<String>> list = new ArrayList<HashSet<String>>();
for...each句式:使用變量s去遍歷list集合中每(each)一個元素
ArrayList<String> list = new ArrayList<String>();
for(String s : list) {
System.out.println(s);
}
5. Collections類(集合工具類)
主要提供了大量的關于集合操作的方法,方便操作集合
- copy(List dest, List src)
返回值:void
將集合src復制到集合dest中
list2一開始必須有不少于list1的數(shù)據(jù),否則會報出異常
public class Test2 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<Integer>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
Random r = new Random();
for(int i = 0; i < 10; i++) {
int x = r.nextInt(100);
list1.add(x);
}
for(int i = 0; i < 10; i++) {
int x = r.nextInt(100);
list2.add(x);
}
System.out.println(list1);
System.out.println(list2);
System.out.println("=======================");
//集合copy
Collections.copy(list2, list1);
System.out.println(list1);
System.out.println(list2);
}
}
- sort(List list)
返回值:void
將集合按照“自然排序”(實際上就是升序)
public class Test3 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<Integer>();
Random r = new Random();
for(int i = 0; i < 10; i++) {
int x = r.nextInt(100);
list1.add(x);
}
System.out.println(list1);
Collections.sort(list1);//升序
System.out.println(list1);
}
}
如果想比較的是自己編寫的類,可以通過讓自己的類實現(xiàn)Comparable接口并完成CompareTo方法
Student類
public class Student implements Comparable<Student>{
private int sno;
private String sname;
public Student() {}
public Student(int sno, String sname) {
this.sno = sno;
this.sname = sname;
}
public int getSno() {
return sno;
}
public void setSno(int sno) {
this.sno = sno;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public int compareTo(Student o) {
if(this.sno < o.sno) {
return -1;
}else if(this.sno == o.sno) {
return 0;
}else {
return 1;
}
}
}
利用sort方法進行排序
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
Student s1 = new Student(1002,"趙四");
Student s2 = new Student(1004,"劉能");
Student s3 = new Student(1001,"謝廣坤");
Student s4 = new Student(1003,"王大拿");
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
for(Student s : list) {
System.out.println(s.getSno()+"\t"+s.getSname());
}
System.out.println("============================");
Collections.sort(list);
for(Student s : list) {
System.out.println(s.getSno()+"\t"+s.getSname());
}
}
}
- sort(List list, Comparator c)
返回值:void
將集合按照“ Comparator”的規(guī)則進行排序
Student類
public class Student{
private int sno;
private String sname;
public Student() {}
public Student(int sno, String sname) {
this.sno = sno;
this.sname = sname;
}
public int getSno() {
return sno;
}
public void setSno(int sno) {
this.sno = sno;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
}
編寫一個比較器
public class StudentComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
if(o1.getSno() < o2.getSno()) {
return -1;
}else if(o1.getSno() == o2.getSno()) {
return 0;
}else {
return 1;
}
}
}
利用sort進行排序
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<Student>();
Student s1 = new Student(1002,"趙四");
Student s2 = new Student(1004,"劉能");
Student s3 = new Student(1001,"謝廣坤");
Student s4 = new Student(1003,"王大拿");
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
for(Student s : list) {
System.out.println(s.getSno()+"\t"+s.getSname());
}
System.out.println("============================");
Collections.sort(list, new StudentComparator());
for(Student s : list) {
System.out.println(s.getSno()+"\t"+s.getSname());
}
}
}
- reverse(List list)
返回值:void
將集合list中的所有數(shù)據(jù)反轉
public class Test3 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<Integer>();
Random r = new Random();
for(int i = 0; i < 10; i++) {
int x = r.nextInt(100);
list1.add(x);
}
System.out.println(list1);
Collections.sort(list1);//升序
System.out.println(list1);
Collections.reverse(list1);//降序
System.out.println(list1);
}
}
