關於 Java Collections 的幾個常見問題數組
來源:linbingdong,性能
linbingdong.com/2017/01/07/Stack%20Overflow上關於Java%20Collections的幾個常見問題/spa
列舉幾個關於Java Collections的常見問題並給出答案。線程
1. 何時用LinkedList,何時用ArrayList?指針
ArrayList是使用數組實現的list,本質上就是數組。ArrayList中的元素能夠經過索引隨機獲取一個元素。可是若是該數組已滿,當添加新元素時須要分配一個新的數組而後將原來數組的元素移動過去,須要O(n)的時間複雜度。添加或刪除一個元素須要移動數組中的其餘元素。這是ArrayList最大的缺點。對象
LinkedList是一個雙向鏈表。所以,當須要獲取list中某個元素,須要從頭至尾遍歷list。另外一方面,在鏈表中添加或刪除元素很快,只須要O(1)的時間複雜度。從空間上來講,在鏈表中一個節點須要兩個額外的指針來指向它的previous和next節點。排序
總結:索引
從時間複雜度來講,若是對list增長或刪除操做較多,優先用LinkedList;若是查詢操做較多,優先用ArrayList。接口
從空間複雜度來講,LinkedList會佔用較多空間。隊列
2. 如何邊遍歷邊移除Collection中的元素
邊遍歷邊修改Collection的惟一正確方式是使用Iterator.remove()方法,以下:
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
// do something
it.remove();
}
一種最多見的錯誤代碼以下:
for(Integer i : list){
list.remove(i)
}
運行以上錯誤代碼會報ConcurrentModificationException異常。這是由於當使用foreach(for(Integer i : list))語句時,會自動生成一個iterator來遍歷該list,但同時該list正在被Iterator.remove()修改。在Java中,通常不容許一個線程在遍歷collection時另外一個線程在修改它。
3. 如何將List轉化成int[]?
不少人可能認爲只需用List.toArray()便可,其實否則。List.toArray()方法只可能獲得Integer[],沒法獲得int[]。
最簡單的方法是使用Apache Commons Lang庫中的ArrayUtils。
int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0]));
在JDK中,沒有捷徑。須要注意的是,不能直接使用List.toArray(),由於這樣會將List轉化成Integer[]而不是int[]。正確的作法以下:
int[] array = new int<div class="list "></div>;
for(int i = 0; i < list.size(); i++){
array[i] = list.get(i);
}
4. 如何將int[]轉化成List?
同上,不少人覺得只需用Arrays.asList()便可,其實否則。由於不能以int[]做爲該方法的參數,要的話也只能是Integer[]。
關於Arrays.asList()方法有以下特性:
1. 該方法對於基本數據類型的數組支持並很差,當數組是基本數據類型時不建議使用
2. 當使用asList()方法時,數組就和列表連接在一塊兒了。當更新其中之一時,另外一個將自動得到更新。由於asList得到的List實際引用的就是數組 注意:僅僅針對對象數組類型,基本數據類型數組不具有該特性。
3. asList獲得的數組是的沒有add和remove方法的。由於asList返回的List是Arrays中的內部類,而該類並無定義add和remove方法。
那麼如何將int[]轉化成List呢?
仍是得本身實現:
int[] array = {1,2,3,4,5};
List<Integer> list = new ArrayList<Integer>();
for(int i: array) {
list.add(i);
}
5. 過濾一個Collection最好的方法是什麼?
如過濾掉list中大於5的整數。
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
int i = it.next();
if(i > 5) { //過濾掉大於5的整數
it.remove();
}
}
6. 將List轉化成Set最簡單的方法?
有兩種方法,取決於你怎麼要怎麼定義兩個元素相等。第一種方法是將list放入HashSet裏,該方法元素是否相等是經過它們的hashCode()來比較的。若是須要本身定義比較的方法,須要用TreeSet。
Set<Integer> set = new HashSet<Integer>(list);
Set<Integer> set = new TreeSet<Integer>(aComparator);
set.addAll(list);
7. 如何刪除ArrayList中重複的元素?
若是不關心元素在ArrayList中的順序,能夠將list放入set中來刪除重複元素,而後在放回list。
Set<Integer> set = new HashSet<Integer>(list);
list.clear();
list.addAll(set);
若是關心元素在ArrayList中的順序,能夠用LinkedHashSet。
8. 有序的collection
Java裏有不少方法來維持一個collection有序。有的須要實現Comparable接口,有的須要本身指定Comparator。
· Collections.sort()能夠用來對list排序。該排序是穩定的,而且能夠保證nlog(n)的性能。
· PriorityQueue提供排序的隊列。PriorityQueue和Collections.sort()的區別是,PriorityQueue動態維護一個有序的隊列(每添加或刪除一個元素就會從新排序),可是隻能獲隊列中的頭元素。
· 若是collection中沒有重複的元素,TreeSet是另外一個選擇。跟PriorityQueue同樣的是,TreeSet也動態維護一個有序的集合。能夠從TreeSet中獲取最大和最小的元素。
總結:Collections.sort()提供一個一次排序的list。PriorityQueue和TreeSet動態維護排序的collection。
9. 拷貝list
有兩種方法能夠用來拷貝list。一種是使用ArrayList構造器。
ArrayList<Integer> dstList = new ArrayList<Integer>(srcList);
另外一種是使用Collections.copy()。
ArrayList<Integer> dstList = new ArrayList<Integer>(srcList.size());
Collections.copy(dstList, srcList);
須要注意的是,使用該方法的話目標list至少跟源list長度同樣長。不然會報IndexOutOfBoundsException異常。
另外有兩點須要注意:
1. 兩種方法都是淺拷貝;
2. Collections.copy()方法的兩個參數必須都是list,而ArrayList方法參數只要是collection便可,所以ArrayList方法更通用。