在實際的項目開發中會有很多的對象,如何高效、方便地管理對象,成為影響程序性能與可維護性的重要環節。 Java 提供了集合框架來解決此類問題,線性表、鍊錶、哈希表等是常用的數據結構,在進行Java 開發時,JDK 已經為我們提供了一系列相應的類來實現基本的數據結構,所有類都在java.util 這個包裡,清單1 描述了集合類的關係。
清單1.集合類之間關係
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap
集合接口
Collection 接口
Collection 是最基本的集合接口,一個Collection 代表一組Object,即Collection 的元素(Elements)。一些Collection 允許相同的元素、支持對元素進行排序,另一些則不行。 JDK 不提供直接繼承自Collection 的類,JDK 提供的類都是繼承自Collection 的子接口,如List 和Set。所有實現Collection 接口的類都必須提供兩個標準的構造函數,無參數的構造函數用於創建一個空的Collection,有一個Collection 參數的構造函數用於創建一個新的Collection,這個新的Collection 與傳入的Collection 有相同的元素,後一個構造函數允許用戶複製一個Collection。
如何遍歷Collection 中的每一個元素?
不論Collection 的實際類型如何,它都支持一個iterator() 的方法,該方法返回一個迭代子,使用該迭代子即可逐一訪問Collection 中每一個元素。典型的用法如下:
Iterator it = collection.iterator(); // 獲得一個迭代子while(it.hasNext()){ Object obj = it.next(); // 得到下一個元素}
Collection 接口派生的兩個接口是List 和Set。
Collection 接口提供的主要方法:
1、boolean add(Object o) 添加對像到集合;
2、boolean remove(Object o) 刪除指定的對象;
3、int size() 返回當前集合中元素的數量;
4、boolean contains(Object o) 查找集合中是否有指定的對象;
5、boolean isEmpty() 判斷集合是否為空;
6、Iterator iterator() 返回一個迭代器;
7、boolean containsAll(Collection c) 查找集合中是否有集合C 中的元素;
8、boolean addAll(Collection c) 將集合C 中所有的元素添加給該集合;
9、void clear() 刪除集合中所有元素;
10、void removeAll(Collection c) 從集合中刪除C 集合中也有的元素;
11、void retainAll(Collection c) 從集合中刪除集合C 中不包含的元素。
List 接口
List 是有序的Collection,使用此接口能夠精確的控制每個元素插入的位置。用戶能夠使用索引(元素在List 中的位置,類似於數組下標)來訪問List 中的元素,這類似於Java 的數組。和下文要提到的Set 不同,List 允許有相同的元素。
除了具有Collection 接口必備的iterator() 方法外,List 還提供一個listIterator() 方法,返回一個ListIterator 接口。和標準的Iterator 接口相比,ListIterator 多了一些add() 之類的方法,允許添加、刪除、設定元素、向前或向後遍歷等功能。實現List 接口的常用類有LinkedList,ArrayList,Vector 和Stack 等。
List 接口提供的主要方法:
1、void add(int index,Object element) 在指定位置上添加一個對象;
2、boolean addAll(int index,Collection c) 將集合C 的元素添加到指定的位置;
3、Object get(int index) 返回List 中指定位置的元素;
4、int indexOf(Object o) 返回第一個出現元素O 的位置;
5、Object removeint(int index) 刪除指定位置的元素;
6、Object set(int index,Object element) 用元素element 取代位置index 上的元素, 返回被取代的元素。
Map 接口
Map 沒有繼承Collection 接口。 Map 提供Key 到Value 的映射,一個Map 中不能包含相同的Key,每個Key 只能映射一個Value。 Map 接口提供3 種集合的視圖,Map 的內容可以被當作一組Key 集合,一組Value 集合,或者一組Key-Value 映射。
Map 提供的主要方法:
1、boolean equals(Object o) 比較對象;
2、boolean remove(Object o) 刪除一個對象;
3、put(Object key,Object value) 添加key 和value。
RandomAccess 接口
RandomAccess 接口是一個標誌接口,本身並沒有提供任何方法,任務凡是通過調用RandomAccess 接口的對像都可以認為是支持快速隨機訪問的對象。此接口的主要目的是標識那些可支持快速隨機訪問的List 實現。任何一個基於數組的List 實現都實現了RaodomAccess 接口,而基於鍊錶的實現則都沒有。因為只有數組能夠進行快速的隨機訪問,而對鍊錶的隨機訪問需要進行鍊錶的遍歷。因此,此接口的好處是,可以在應用程序中知道正在處理的List 對像是否可以進行快速隨機訪問,從而針對不同的List 進行不同的操作,以提高程序的性能。
集合類介紹
LinkedList 類
LinkedList 實現了List 接口,允許Null 元素。此外LinkedList 提供額外的Get、Remove、Insert 等方法在LinkedList 的首部或尾部操作數據。這些操作使得LinkedList 可被用作堆棧(Stack)、隊列(Queue)或雙向隊列(Deque)。請注意LinkedList 沒有同步方法,它不是線程同步的,即如果多個線程同時訪問一個List,則必須自己實現訪問同步。一種解決方法是在創建List 時構造一個同步的List,方法如
List list = Collections.synchronizedList(new LinkedList(…));
ArrayList 類
ArrayList 實現了可變大小的數組。它允許所有元素,包括Null。 Size、IsEmpty、Get、Set 等方法的運行時間為常數,但是Add 方法開銷為分攤的常數,添加N 個元素需要O(N) 的時間,其他的方法運行時間為線性。
每個ArrayList 實例都有一個容量(Capacity),用於存儲元素的數組的大小,這個容量可隨著不斷添加新元素而自動增加。當需要插入大量元素時,在插入前可以調用ensureCapacity 方法來增加ArrayList 的容量以提高插入效率。和LinkedList 一樣,ArrayList 也是線程非同步的(unsynchronized)。
ArrayList 提供的主要方法:
1、Boolean add(Object o) 將指定元素添加到列表的末尾;
2、Boolean add(int index,Object element) 在列表中指定位置加入指定元素;
3、Boolean addAll(Collection c) 將指定集合添加到列表末尾;
4、Boolean addAll(int index,Collection c) 在列表中指定位置加入指定集合;
5、Boolean clear() 刪除列表中所有元素;
6、Boolean clone() 返回該列表實例的一個拷貝;
7、Boolean contains(Object o) 判斷列表中是否包含元素;
8、Boolean ensureCapacity(int m) 增加列表的容量,如果必須,該列表能夠容納m 個元素;
9、Object get(int index) 返回列表中指定位置的元素;
10、Int indexOf(Object elem) 在列表中查找指定元素的下標;
11、Int size() 返回當前列表的元素個數。
Vector 類
Vector 非常類似於ArrayList,區別是Vector 是線程同步的。由Vector 創建的Iterator,雖然和ArrayList 創建的Iterator 是同一接口,但是,因為Vector 是同步的,當一個Iterator 被創建而且正在被使用,另一個線程改變了Vector 的狀態(例如,添加或刪除了一些元素),這時調用Iterator 的方法時將拋出ConcurrentModificationException,因此必須捕獲該異常。
Stack 類
Stack 繼承自Vector,實現了一個後進先出的堆棧。 Stack 提供5 個額外的方法使得Vector 得以被當作堆棧使用。除了基本的Push 和Pop 方法,還有Peek 方法得到棧頂的元素,Empty 方法測試堆棧是否為空,Search 方法檢測一個元素在堆棧中的位置。注意,Stack 剛創建後是空棧。
Set 類
Set 是一種不包含重複的元素的Collection,即任意的兩個元素e1 和e2 都有e1.equals(e2)=false。 Set 最多有一個null 元素。很明顯,Set 的構造函數有一個約束條件,傳入的Collection 參數不能包含重複的元素。請注意,必須小心操作可變對象(Mutable Object),如果一個Set 中的可變元素改變了自身狀態,這可能會導致一些問題。
Hashtable 類
Hashtable 繼承Map 接口,實現了一個基於Key-Value 映射的哈希表。任何非空(non-null)的對像都可作為Key 或者Value。添加數據使用Put(Key,Value),取出數據使用Get(Key),這兩個基本操作的時間開銷為常數。
Hashtable 通過Initial Capacity 和Load Factor 兩個參數調整性能。通常缺省的Load Factor 0.75 較好地實現了時間和空間的均衡。增大Load Factor 可以節省空間但相應的查找時間將增大,會影響像Get 和Put 這樣的操作。使用Hashtable 的簡單示例,將1、2、3 這三個數字放到Hashtable 裡面,他們的Key 分別是”one”、”two”、”three”,代碼如清單2 所示。
清單2 .Hashtable 示例
Hashtable numbers = new Hashtable();numbers.put(“one”, new Integer(1));numbers.put(“two”, new Integer(2));numbers.put(“three”, new Integer(3 ));
如果我們需要取出一個數,比如2,可以用相應的key 來取出,代碼如清單3 所示。
清單3.從Hastable 讀取數據
Integer n = (Integer)numbers.get(“two”);System.out.println(“two =”+ n);
由於作為Key 的對象將通過計算其散列函數來確定與之對應的Value 的位置,因此任何作為key 的對像都必須實現HashCode 和Equals 方法。 HashCode 和Equals 方法繼承自根類Object,如果你用自定義的類當作Key 的話,要相當小心,按照散列函數的定義,如果兩個對象相同,即obj1.equals(obj2)=true,則它們的HashCode 必須相同,但如果兩個對像不同,則它們的HashCode 不一定不同,如果兩個不同對象的HashCode 相同,這種現象稱為衝突,衝突會導致操作哈希表的時間開銷增大,所以盡量定義好的HashCode() 方法,能加快哈希表的操作。
如果相同的對像有不同的HashCode,對哈希表的操作會出現意想不到的結果(期待的Get 方法返回Null),要避免這種問題,最好同時復寫Equals 方法和HashCode 方法,而不要只寫其中一個。
HashMap 類
HashMap 和Hashtable 類似,不同之處在於HashMap 是線程非同步的,並且允許Null,即Null Value 和Null Key。但是將HashMap 視為Collection 時(values() 方法可返回Collection),其迭代子操作時間開銷和HashMap 的容量成比例。因此,如果迭代操作的性能相當重要的話,不要將HashMap 的初始化容量設得過高,或者Load Factor 參數設置過低。
WeakHashMap 類
WeakHashMap 是一種改進的HashMap,它對Key 實行“弱引用”,如果一個Key 不再被外部所引用,那麼該Key 可以被GC 回收。
集合類實踐
ArrayList、Vector、LinkedList 均來自AbstractList 的實現,而AbstractList 直接實現了List 接口,並擴展自AbstarctCollection。 ArrayList 和Vector 使用了數組實現,ArrayList 沒有對任何一個方法提供線程同步,因此不是線程安全的,Vector 中絕大部分方法都做了線程同步,是一種線程安全的實現。 LinkedList 使用了循環雙向鍊錶數據結構,由一系列表項連接而成,一個表項總是包含3 個部分,元素內容、前驅表項和後驅表項。
當ArrayList 對容量的需求超過當前數組的大小時,需要進行擴容。擴容過程中,會進行大量的數組複製操作,而數組複製時,最終將調用System.arraycopy() 方法。 LinkedList 由於使用了鍊錶的結構,因此不需要維護容量的大小,然而每次的元素增加都需要新建一個Entry 對象,並進行更多的賦值操作,在頻繁的系統調用下,對性能會產生一定的影響,在不間斷地生成新的對像還是佔用了一定的資源。而因為數組的連續性,因此總是在尾端增加元素時,只有在空間不足時才產生數組擴容和數組複製。
ArrayList 是基於數組實現的,而數組是一塊連續的內存空間,如果在數組的任意位置插入元素,必然導致在該位置後的所有元素需要重新排列,因此其效率較差,盡可能將數據插入到尾部。 LinkedList 不會因為插入數據導致性能下降。
ArrayList 的每一次有效的元素刪除操作後都要進行數組的重組,並且刪除的元素位置越靠前,數組重組時的開銷越大,要刪除的元素位置越靠後,開銷越小。 LinkedList 要移除中間的數據需要便利完半個List。
清單4. ArrayList 和LinkedList 使用代碼
import java.util.ArrayList;import java.util.LinkedList; public class ArrayListandLinkedList { public static void main(String[] args){ long start = System.currentTimeMillis(); ArrayList list = new ArrayList(); Object obj = new Object(); for(int i=0;i<5000000;i++){ list.add(obj); } long end = System.currentTimeMillis(); System.out.println(end-start); start = System. currentTimeMillis(); LinkedList list1 = new LinkedList(); Object obj1 = new Object(); for(int i=0;i<5000000;i++){ list1.add(obj1); } end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); Object obj2 = new Object(); for(int i=0;i<1000;i++){ list.add(0,obj2); } end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); Object obj3 = new Object(); for(int i=0;i<1000;i++){ list1.add(obj1); } end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); list.remove(0); end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); list1.remove(250000); end = System.currentTimeMillis(); System.out.println(end-start); }}
清單5. 運行輸出
639129669690015
HashMap 是將Key 做Hash 算法,然後將Hash 值映射到內存地址,直接取得Key 所對應的數據。在HashMap 中,底層數據結構使用的是數組,所謂的內存地址即數組的下標索引。 HashMap 的高性能需要保證以下幾點:
1、Hash 算法必須是高效的;
2、Hash 值到內存地址(數組索引) 的算法是快速的;
3、根據內存地址(數組索引) 可以直接取得對應的值。
HashMap 實際上是一個鍊錶的數組。前面已經介紹過,基於HashMap 的鍊錶方式實現機制,只要HashCode() 和Hash() 方法實現得足夠好,能夠盡可能地減少衝突的產生,那麼對HashMap 的操作幾乎等價於對數組的隨機訪問操作,具有很好的性能。但是,如果HashCode() 或者Hash() 方法實現較差,在大量衝突產生的情況下,HashMap 事實上就退化為幾個鍊錶,對HashMap 的操作等價於遍歷鍊錶,此時性能很差。
HashMap 的一個功能缺點是它的無序性,被存入到HashMap 中的元素,在遍歷HashMap 時,其輸出是無序的。如果希望元素保持輸入的順序,可以使用LinkedHashMap 替代。
LinkedHashMap 繼承自HashMap,具有高效性,同時在HashMap 的基礎上,又在內部增加了一個鍊錶,用以存放元素的順序。
HashMap 通過hash 算法可以最快速地進行Put() 和Get() 操作。 TreeMap 則提供了一種完全不同的Map 實現。從功能上講,TreeMap 有著比HashMap 更為強大的功能,它實現了SortedMap 接口,這意味著它可以對元素進行排序。 TreeMap 的性能略微低於HashMap。如果在開發中需要對元素進行排序,那麼使用HashMap 便無法實現這種功能,使用TreeMap 的迭代輸出將會以元素順序進行。 LinkedHashMap 是基於元素進入集合的順序或者被訪問的先後順序排序,TreeMap 則是基於元素的固有順序(由Comparator 或者Comparable 確定)。
LinkedHashMap 是根據元素增加或者訪問的先後順序進行排序,而TreeMap 則根據元素的Key 進行排序。
清單6 所示代碼演示了使用TreeMap 實現業務邏輯的排序。
清單6. TreeMap 實現排序
import java.util.Iterator;import java.util.Map;import java.util.TreeMap; public class Student implements Comparable<Student>{ public String name;public int score;public Student(String name,int score){this. name = name;this.score = score;} @Override//告訴TreeMap 如何排序public int compareTo(Student o) {// TODO Auto-generated method stubif(o.score<this.score){return 1;}else if(o.score>this.score){return -1;}return 0;} @Overridepublic String toString(){StringBuffer sb = new StringBuffer();sb.append("name:");sb.append(name );sb.append(" ");sb.append("score:");sb.append(score);return sb.toString();} public static void main(String[] args){TreeMap map = new TreeMap();Student s1 = new Student("1",100);Student s2 = new Student("2",99);Student s3 = new Student("3",97);Student s4 = new Student(" 4",91);map.put(s1, new StudentDetailInfo(s1));map.put(s2, new StudentDetailInfo(s2));map.put(s3, new StudentDetailInfo(s3));map.put(s4 , new StudentDetailInfo(s4)); //打印分數位於S4 和S2 之間的人Map map1=((TreeMap)map).subMap(s4, s2);for(Iterator iterator=map1.keySet().iterator( );iterator.hasNext();){Student key = (Student)iterator.next();System.out.println(key+"->"+map.get(key));}System.out.println(" subMap end"); //打印分數比s1 低的人map1=((TreeMap)map).headMap(s1);for(Iterator iterator=map1.keySet().iterator();iterator.hasNext();) {Student key = (Student)iterator.next();System.out.println(key+"->"+map.get(key));}System.out.println("subMap end"); //打印分數比s1 高的人map1=((TreeMap)map).tailMap(s1);for(Iterator iterator=map1.keySet().iterator();iterator.hasNext();){Student key = (Student)iterator. next();System.out.println(key+"->"+map.get(key));}System.out.println("subMap end");} } class StudentDetailInfo{Student s;public StudentDetailInfo(Student s ){this.s = s;}@Overridepublic String toString(){return s.name + "'s detail information";}}
清單7 .運行輸出
name:4 score:91->4's detail informationname:3 score:97->3's detail informationsubMap endname:4 score:91->4's detail informationname:3 score:97->3's detail informationname:2 score:99->2's detail informationsubMap endname:1 score:100->1's detail informationsubMap end
WeakHashMap 特點是當除了自身有對Key 的引用外,如果此Key 沒有其他引用,那麼此Map 會自動丟棄該值。如清單8 所示代碼聲明了兩個Map 對象,一個是HashMap,一個是WeakHashMap,同時向兩個map 中放入A、B 兩個對象,當HashMap 刪除A,並且A、B 都指向Null 時, WeakHashMap 中的A 將自動被回收掉。出現這個狀況的原因是,對於A 對象而言,當HashMap 刪除並且將A 指向Null 後,除了WeakHashMap 中還保存A 外已經沒有指向A 的指針了,所以WeakHashMap 會自動捨棄掉a,而對於B 對象雖然指向了null,但HashMap 中還有指向B 的指針,所以WeakHashMap 將會保留B 對象。
清單8.WeakHashMap 示例代碼
import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.WeakHashMap; public class WeakHashMapTest { public static void main(String[] args) throws Exception { String a = new String ("a"); String b = new String("b"); Map weakmap = new WeakHashMap(); Map map = new HashMap(); map.put(a, "aaa"); map.put(b, "bbb"); weakmap.put(a, "aaa"); weakmap.put(b, "bbb"); map.remove(a); a=null; b=null; System.gc(); Iterator i = map.entrySet().iterator(); while (i.hasNext()) { Map.Entry en = (Map.Entry)i.next(); System.out.println("map:"+en.getKey ()+":"+en.getValue()); } Iterator j = weakmap.entrySet().iterator(); while (j.hasNext()) { Map.Entry en = (Map.Entry)j.next (); System.out.println("weakmap:"+en.getKey()+":"+en.getValue()); } }}
清單9 .運行輸出
map:b:bbbweakmap:b:bbb
WeakHashMap 主要通過expungeStaleEntries 這個函數來實現移除其內部不用的條目,從而達到自動釋放內存的目的。基本上只要對WeakHashMap 的內容進行訪問就會調用這個函數,從而達到清除其內部不再為外部引用的條目。但是如果預先生成了WeakHashMap,而在GC 以前又不曾訪問該WeakHashMap, 那不是就不能釋放內存了嗎?
清單10. WeakHashMapTest1
import java.util.ArrayList;import java.util.List;import java.util.WeakHashMap; public class WeakHashMapTest1 { public static void main(String[] args) throws Exception { List<WeakHashMap<byte[][], byte[ ][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>(); for (int i = 0; i < 1000; i++) { WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>(); d.put(new byte[1000][1000], new byte[1000][1000]); maps .add(d); System.gc(); System.err.println(i); } }}
不改變任何JVM 參數的情況運行清單10 所示代碼,由於Java 默認內存是64M,拋出內存溢出了錯誤。
清單11. 運行輸出
241242243Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat WeakHashMapTest1.main(WeakHashMapTest1.java:10)
果不其然,WeakHashMap 這個時候並沒有自動幫我們釋放不用的內存。清單12 所示代碼不會出現內存溢出問題。
清單12. WeakHashMapTest2
import java.util.ArrayList;import java.util.List;import java.util.WeakHashMap; public class WeakHashMapTest2 { public static void main(String[] args) throws Exception { List<WeakHashMap<byte[][], byte[ ][]>> maps = new ArrayList<WeakHashMap<byte[][], byte[][]>>(); for (int i = 0; i < 1000; i++) { WeakHashMap<byte[][], byte[][]> d = new WeakHashMap<byte[][], byte[][]>(); d.put(new byte[1000][1000], new byte[1000][1000]); maps .add(d); System.gc(); System.err.println(i); for (int j = 0; j < i; j++) { System.err.println(j + " size" + maps.get (j).size()); } } }}
運行結果發現這次測試輸出正常, 不再出現內存溢出問題。
總的來說,WeakHashMap 並不是你什麼也乾它就能自動釋放內部不用的對象的,而是在你訪問它的內容的時候釋放內部不用的對象。
WeakHashMap 實現弱引用,是因為它的Entry<K,V>是繼承自WeakReference<K>的,
在WeakHashMap$Entry<K,V>的類定義及構造函數里面如清單13 所示。
清單13. WeakHashMap 類定義
private static class Entry<K,V> extends WeakReference<K>implements Map.Entry<K,V> Entry(K key, V value, ReferenceQueue<K> queue,int hash, Entry<K,V> next) {super (key, queue);this.value = value;this.hash = hash;this.next = next;}
請注意它構造父類的語句:“super(key, queue);”,傳入的是Key,因此Key 才是進行弱引用的,Value 是直接強引用關聯在this.value 之中。在System.gc() 時,Key 中的Byte 數組進行了回收,而Value 依然保持(Value 被強關聯到Entry 上,Entry 又關聯在Map 中,Map 關聯在ArrayList 中)。
For 循環中每次都New 一個新的WeakHashMap,在Put 操作後,雖然GC 將WeakReference 的Key 中的Byte 數組回收了,並將事件通知到了ReferenceQueue,但後續卻沒有相應的動作去觸發WeakHashMap 去處理ReferenceQueue ,所以WeakReference 包裝Key 依然存在於WeakHashMap 中,其對應的value 也當然存在。
那value 是何時被清除的呢? 對清單10 和清單11 兩個示例程序進行分析可知,清單11 的maps.get(j).size() 觸發了Value 的回收,那又如何觸發的呢?查看WeakHashMap 源碼可知,Size 方法調用了expungeStaleEntries 方法,該方法對JVM 要回收的的Entry(Quene 中) 進行遍歷,並將Entry 的Value 置空,回收了內存。所以效果是Key 在GC 的時候被清除,Value 在Key 清除後訪問WeakHashMap 被清除。
WeakHashMap 類是線程不同步的,可以使用Collections.synchronizedMap 方法來構造同步的WeakHashMap, 每個鍵對象間接地存儲為一個弱引用的指示對象。因此,不管是在映射內還是在映射之外,只有在垃圾回收器清除某個鍵的弱引用之後,該鍵才會自動移除。需要注意的是,WeakHashMap 中的值對象由普通的強引用保持。因此應該小心謹慎,確保值對像不會直接或間接地強引用其自身的鍵,因為這會阻止鍵的丟棄。注意,值對象可以通過WeakHashMap 本身間接引用其對應的鍵,這就是說,某個值對象可能強引用某個其他的鍵對象,而與該鍵對象相關聯的值對象轉而強引用第一個值對象的鍵。
處理此問題的一種方法是,在插入前將值自身包裝在WeakReferences 中,如:m.put(key, new WeakReference(value)),然後,分別用get 進行解包,該類所有“collection 視圖方法”返回的迭代器均是快速失敗的,在迭代器創建之後,如果從結構上對映射進行修改,除非通過迭代器自身的Remove 或Add 方法,其他任何時間任何方式的修改,迭代器都將拋出ConcurrentModificationException。因此,面對並發的修改,迭代器很快就完全失敗,而不是冒著在將來不確定的時間任意發生不確定行為的風險。
注意,我們不能確保迭代器不失敗,一般來說,存在不同步的並發修改時,不可能做出任何完全確定的保證。
總結綜合前面的介紹和實例代碼,我們可以知道,如果涉及到堆棧、隊列等操作,應該考慮用List。對於需要快速插入、刪除元素等操作,應該使用LinkedList。如果需要快速隨機訪問元素,應該使用ArrayList。如果程序在單線程環境中,或者訪問僅僅在一個線程中進行,考慮非同步的類,其效率較高。如果多個線程可能同時操作一個類,應該使用同步的類。要特別注意對哈希表的操作,作為Key 的對像要正確複寫Equals 和HashCode 方法。盡量返回接口而非實際的類型,如返回List 而非ArrayList,這樣如果以後需要將ArrayList 換成LinkedList 時,客戶端代碼不用改變,這就是針對抽象進行編程思想。
本文只是針對應用層面的分享,希望對大家優化Java集合類操作有所幫助。