列舉幾個關於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()
方法,如下:
1 2 3 4 5 |
Iterator<Integer> it = list.iterator();
while (it.hasNext()){
// do something
it.remove();
}
|
一種最常見的錯誤
1 2 3 |
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
。
1 |
int [] array = ArrayUtils.toPrimitive(list.toArray( new Integer[ 0 ]));
|
在JDK中,沒有捷徑。需要註意的是,不能直接使用List.toArray()
,因為這樣會將List轉化成Integer[]而不是int[]。正確的做法如下:
1 2 3 4 |
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呢?
還是得自己實現:
1 2 3 4 5 |
int [] array = { 1 , 2 , 3 , 4 , 5 };
List<Integer> list = new ArrayList<Integer>();
for ( int i: array) {
list.add(i);
}
|
5. 過濾一個Collection最好的方法是什麽?
如過濾掉list中大於5的整數。
1 2 3 4 5 6 7 |
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
。
1 |
Set<Integer> set = new HashSet<Integer>(list);
|
1 2 |
Set<Integer> set = new TreeSet<Integer>(aComparator);
set.addAll(list);
|
7. 如何刪除ArrayList中重復的元素?
如果不關心元素在ArrayList中的順序,可以將list放入set中來刪除重復元素,然後在放回list。
1 2 3 |
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
構造器。
1 |
ArrayList<Integer> dstList = new ArrayList<Integer>(srcList);
|
另一種是使用Collections.copy()
。
1 2 |
ArrayList<Integer> dstList = new ArrayList<Integer>(srcList.size());
Collections.copy(dstList, srcList);
|
需要註意的是,使用該方法的話目標list至少跟源list長度一樣長。否則會報IndexOutOfBoundsException
異常。
另外有兩點需要註意:
- 兩種方法都是淺拷貝
Collections.copy()
方法的兩個參數必須都是list,而ArrayList
方法參數只要是collection即可,因此ArrayList
方法更通用。
列舉幾個關於Java Collections的常見問題並給出答案