本文主要介紹JAVA中常見容器間的關係和主要區別。JAVA中的容器種類很多,且各有特點。爲此特意進行學習研究,寫下此文,作爲一點總結。若有錯誤,歡迎拍磚。
上圖是JAVA常見的各個容器的繼承關係,我們就順着繼承關係說一下各個接口或者類的特點吧。
Iterable 接口
-
Iterable是一個超級接口,被Collection所繼承。它只有一個方法: Iterator<T> iterator() //即返回一個迭代器
-
迭代器是一種設計模式,它是一個對象,它可以遍歷並選擇序列中的對象,而開發人員不需要了解該序列的底層結構。迭代器通常被稱爲**“輕量級”**對象,因爲創建它的代價小。
-
Java中的Iterator功能比較簡單,並且只能單向移動:
(1) 使用方法iterator()要求容器返回一個Iterator。第一次調用Iterator的next()方法時,它返回序列的第一個元素。注意:iterator()方法是java.lang.Iterable接口,被Collection繼承。
(2) 使用next()獲得序列中的下一個元素。
(3) 使用hasNext()檢查序列中是否還有元素。
(4) 使用remove()將迭代器新返回的元素刪除。 -
Iterator是Java迭代器最簡單的實現,爲List設計的ListIterator具有更多的功能,它可以從兩個方向遍歷List,也可以從List中插入和刪除元素。
舉一個例子來說明迭代器的用法:
public static void main(String args[]) {
List<String> l = new ArrayList<String>();
l.add("aa");
l.add("bb");
l.add("cc");
Iterator iter = l.iterator();
while(iter.hasNext()){
System.out.println((String)iter.next());
}
// for循環的版本
// for(Iterator<String> iter=l.iterator();iter.hasNext();){
// String str = (String)iter.next();
// System.out.println(str);
// }
}
運行結果:
很明顯,iterator用於while循環更方便簡潔一些。
Collection 接口
我們直接打開API文檔進行查看
文檔中寫道,JDK 不提供此接口的任何直接 實現:它提供更具體的子接口(如 Set 和 List)實現。也就是一般不會直接使用Collection,而是會使用它的子類,如List或Set。
在圖中我標註了4點,不同的Collection子類對於有序性、重複性、null、線程同步都有不同的策略。基於此,Collection的介紹就這樣,下面就其具體的子類來進行介紹。
List 接口
-
List是有序的 collection(也稱爲序列)。此接口的用戶可以對列表中每個元素的插入位置進行精確地控制。用戶可以根據元素的整數索引(在列表中的位置)訪問元素,並搜索列表中的元素。
-
用戶插入的順序或者指定的位置就是元素插入的位置。它與Set不同,List允許插入重複的值。
-
List 接口提供了特殊的迭代器,稱爲 ListIterator,除了允許 Iterator 接口提供的正常操作外,該迭代器還允許元素插入和替換,以及雙向訪問。還提供了一個方法(如下)來獲取從列表中指定位置開始的列表迭代器。
ListIterator <E> listIterator(int index)
返回列表中元素的列表迭代器(按適當順序),從列表的指定位置開始
-
List 接口提供了兩種搜索指定對象的方法。從性能的觀點來看,應該小心使用這些方法。在很多實現中,它們將執行高開銷的線性搜索。
-
List 接口提供了兩種在列表的任意位置高效插入和移除多個元素的方法。
-
List的子類
- ArrayLis是基於數組實現的List類,它封裝了一個動態的、增長的、允許再分配的Object[ ]數組.它允許對元素進行快速隨機訪問
- 當從ArrayList的中間位置插入或者刪除元素時,需要對數組進行復制、移動、代價比較高。因此,它適合隨機查找和遍歷,不適合插入和刪除。
- Vector與ArrayList一樣,也是通過數組實現的,不同的是它支持線程的同步,即某一時刻只有一個線程能夠寫Vector,避免多線程同時寫而引起的不一致性,但實現同步需要很高的花費,因此,訪問它比訪問ArrayList慢。所以現在已經不太常用了
1.2.1)Stack (類)
Stack是Vector提供的一個子類,用於模擬"棧"這種數據結構(LIFO後進先出)
- LinkedList是用鏈表結構存儲數據的,很適合數據的動態插入和刪除,隨機訪問和遍歷速度比較慢。
- 另外,它還實現了Deque接口,專門用於操作表頭和表尾元素,可以當作堆棧、隊列和雙向隊列使用。
關於ArrayList和Vector的區別,再次回顧一下:
名稱 擴容方式(默認) 線程安全 速度 有效個數的屬性 ArrayList 增長50% 線程不安全 快 size Vector 增長一倍 線程安全 慢 elementCount 共同點 如果新增的有效元素個數超過數組本身的長度,都會導致數組進行擴容 - remove,add(index,obj)方法都會導致內部數組進行數據拷貝的操作,這樣在大數據量時,可能會影響效率 -
- ArrayLis是基於數組實現的List類,它封裝了一個動態的、增長的、允許再分配的Object[ ]數組.它允許對元素進行快速隨機訪問
寫在後面:List接口的排序可以通過Collections.sort()來進行定製排序。
只需要繼承Comparable接口後,重寫compareTo()**方法。
代碼如下:
public class Student extends Thread implements Comparable {
private int age;
private String name;
private String tel;
private int height;
public Student( String name,int age, String tel, int height) {
this.age = age;
this.name = name;
this.tel = tel;
this.height = height;
}
public static void main(String args[]) throws InterruptedException{
Student stu1 = new Student("張三",21,"156482",172);
Student stu2 = new Student("李四",18,"561618",168);
Student stu3 = new Student("王五",19,"841681",170);
Student stu4 = new Student("趙七",20,"562595",180);
List<Student> list = new ArrayList<Student>();
//亂序插入
list.add(stu3);
list.add(stu1);
list.add(stu4);
list.add(stu2);
System.out.println("-----------排序前----------");
Iterator<Student> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
/*
* 使用Collections的sort方法讓集合排序,使用其方法必須要重寫
* Comparable接口的compareTo()方法
* */
Collections.sort(list);
System.out.println("-----------按照年齡排序後----------");
for(int i=0;i<list.size();i++){
System.out.println(list.get(i).toString());
}
}
//重寫compareTo方法,用age來比較。也可以用別的來比較
@Override
public int compareTo(Object o) {
//使用當前對象的年齡和其他對象的年齡比較,如果<0返回負數,>0返回正數,=0返回0
int z = this.age - ((Student)o).getAge();
if(z<0)
return -1; // 返回其他負數也行
else if(z == 0)
return 0;
else
return 1; //返回其他正數也行
}
//重寫toString,便於輸出
@Override
public String toString(){
return name+","+age+","+tel+","+height;
}
輸出結果如下:
Set接口
- Set,顧名思義,集合的意思。java的集合和數學的集合一樣,滿足集合的無序性,確定性,單一性。所以可以很好的理解,Set是無序、不可重複的。同時,如果有多個null,則不滿足單一性了,所以Set只能有一個null。
- Set類似於一個罐子,丟進Set的元素沒有先後的差別。
- Set判斷兩個對象相同不是使用"=="運算符,而是根據equals方法。也就是說,我們在加入一個新元素的時候,如果這個新元素對象和Set中已有對象進行注意equals比較都返回false,則Set就會接受這個新元素對象,否則拒絕。
——因爲Set的這個制約,在使用Set集合的時候,應該注意兩點:- 爲Set集合裏的元素的實現類實現一個有效的equals(Object)方法;
- 對Set的構造函數,傳入的Collection參數不能包含重複的元素。
-
Set的子類
- HashSet是Set接口的典型實現,HashSet使用HASH算法來存儲集合中的元素,因此具有良好的存取和查找性能。當向HashSet集合中存入一個元素時,HashSet會調用該對象的 hashCode()方法來得到該對象的hashCode值,然後根據該HashCode值決定該對象在HashSet中的存儲位置。
- 值得主要的是,HashSet集合判斷兩個元素相等的標準是兩個對象通過equals()方法比較相等,並且兩個對象的hashCode()方法的返回值相等
。1.1.1)LinkedHashSet(類)
- LinkedHashSet集合也是根據元素的hashCode值來決定元素的存儲位置,但和HashSet不同的是,它同時使用鏈表維護元素的次序,這樣使得元素看起來是以插入的順序保存的。
- 當遍歷LinkedHashSet集合裏的元素時,LinkedHashSet將會按元素的添加順序來訪問集合裏的元素。
- LinkedHashSet需要維護元素的插入順序,因此性能略低於HashSet的性能,但在迭代訪問Set裏的全部元素時(遍歷)將有很好的性能(鏈表很適合進行遍歷)LinkedHashSet需要維護元素的插入順序,因此性能略低於HashSet的性能,但在迭代訪問Set裏的全部元素時(遍歷)將有很好的性能(鏈表很適合進行遍歷)
Queue 接口
此接口用於模擬“隊列”數據結構(FIFO)。新插入的元素放在隊尾,隊頭存放着保存時間最長的元素。
-
Queue的子類、子接口
簡單回顧一下上述三個接口的區別
容器名 | 是否有序 | 是否可重複 | null的個數 |
---|---|---|---|
List | 有序 | 可重複 | 允許多個null |
Set | 無序 | 不可重複 | 只允許一個null |
Queue | 有序(FIFO) | 可重複 | 通常不允許插入null |
Map 接口
-
Map不是collection的子接口或者實現類。Map是一個接口。
-
Map用於保存具有“映射關係”的數據。每個Entry都持有鍵-值兩個對象。其中,Value可能重複,但是Key不允許重複(和Set類似)。
-
Map可以有多個Value爲null,但是只能有一個Key爲null。
-
Map的子類、子接口
-
1) HashMap (類)
和HashSet集合不能保證元素的順序一樣,HashMap也不能保證key-value對的順序。並且類似於HashSet判斷兩個key是否相等的標準一樣: 兩個key通過equals()方法比較返回true、 同時兩個key的hashCode值也必須相等
-
2) HashTable (類)
是一個古老的Map實現類。
-
3) SortedMap(接口)
如同Set->SortedSet->TreeSet一樣,Map也有Map->SortedMap->TreeMap的繼承關係。
-
4) WeakHashMap(類)
- 看名字就能發現,這是Weak後的HashMap。但是二者大體差別不大。
- 區別在於,HashMap的key保留了對實際對象的強引用,這意味着只要該HashMap對象不被銷燬,該HashMap所引用的對象就不會被垃圾回收。
- 但WeakHashMap的key只保留了對實際對象的弱引用,這意味着如果WeakHashMap對象的key所引用的對象沒有被其他強引用變量所引用,則這些key所引用的對象可能被垃圾回收,當垃圾回收了該key所對應的實際對象之後,WeakHashMap也可能自動刪除這些key所對應的key-value對。
-
5) IdentityHashMap(類)
- 這個類也和HashMap類似(怎麼那麼多類似的hhhh),區別在於,在IdentityHashMap中,當且僅當兩個key嚴格相等(key1==key2)時,IdentityHashMap才認爲兩個key相等
-
6) EnumMap(類)
- EnumMap是一個與枚舉類一起使用的Map實現,EnumMap中的所有key都必須是單個枚舉類的枚舉值。創建EnumMap時必須顯式或隱式指定它對應的枚舉類。EnumMap根據key的自然順序存儲。