关于 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方法更通用。