列举几个关于 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
- ;
- 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() 提供一个一次排序的 listPriorityQueue 和 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
异常
另外有两点需要注意:
两种方法都是浅拷贝 Collections.copy() 方法的两个参数必须都是 list, 而 ArrayList 方法参数只要是 collection 即可, 因此 ArrayList 方法更通用
来源: http://www.jqhtml.com/14262.html