以下是一些在Stackoverflow上经常被问起的与Java集合相关的问题。在你查阅这些问题之前,最好先去看看【Simple Java】Java集合框架的接口和类层次关系结构图

什么时候优先选择LinkedList,而不是ArrayList

ArrayList本质上是一个数组,它的元素可以直接通过索引访问。但是,当数组满的时候,需要申请新的更大的数组空间,并将所有元素复制到新数组中,这将花费O(n)的时间。另外,插入和删除元素需要移动数组中的其它元素,这也许是ArrayList最大的劣势。

LinkedList是一个双向链表,因此,当访问链表中间的元素的时候,它需要从链表的头结点处开始搜索。但好的一方面是,对于插入和删除操作,LinkedList相对更快,因为它只需要改变链表的局部位置。

总的来说,在最差情况下,两者的时间复杂度比较如下:

                           | Arraylist | LinkedList
------------------------------------------
get(index) | O(1) | O(n)
add(E) | O(n) | O(1)
add(E, index) | O(n) | O(n)
remove(index) | O(n) | O(n)
Iterator.remove() | O(n) | O(1)
Iterator.add(E) | O(n) | O(1)

除了运行时间外,内存空间的使用也应该被考虑,特别是对于大的列表。在LinkedList中,每个节点需要两个额外的指针指向前节点和后节点,然而ArrayList只需要一个存放元素的数组即可。

其它List之间的比较,可查阅:【Simple Java】ArrayList vs LinkedList vs Vector

迭代遍历集合的时候,正确的删除元素

在迭代集合的时候,唯一正确的方法修改集合的方法是通过Iterator.remove()方法,如下示例:

Iterator<Integer> itr = list.iterator();
while(itr.hasNext()) {
// do something
itr.remove();
}

另外,举一个常见的错误代码:

for(Integer i: list) {
list.remove(i);
}

运行以上错误代码,你将会得到ConcurrentModificationException异常,这是因为以上代码产生了一个迭代器(由for语句产生)去遍历列表,但是同时,列表被修改了(通过Iterator.remove())。在Java中,一个线程在迭代遍历集合的过程中,是不允许另外一个线程去修改集合的。

怎样将List转成int[]

最简单的方法是使用Apache Commons Lang库下的ArrayUtils工具类,如下:

int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0]));

在JDK中,没有捷径去做以上转换。注意你不能使用List.toArray()方法,因为这将会把List转成Integer[]。正确的方法如下:

int[] array = new int[list.size()];
for(int i=0; i < list.size(); i++) {
array[i] = list.get(i);
}

怎样将int[]转成List

最简单的方法仍然是使用Apache Commons Lang的ArrayUtils工具类,如下:

List list = Arrays.asList(ArrayUtils.toObject(array));

在JDK中,仍然没有捷径,只能使用如下方法:

int[] array = {1,2,3,4,5};
List<Integer> list = new ArrayList<Integer>();
for(int i: array) {
list.add(i);
}

什么是过滤集合最好的方法

同样,你可以使用第三方库,如google的Guava库或Apache Commons Lang去实现这个功能,两者都提供了filter()方法(Guava的Collections2或Apache的CollectionUtils类)。filter()方法会返回匹配的元素。

在JDK中,这将会变得困难,好消息是,在java 8中,增加了Predicate接口,可以实现该功能。但是现在,我们只能使用迭代器去遍历整个集合:

Iterator<Integer> itr = list.iterator();
while(itr.hasNext()) {
int i = itr.next();
if (i > 5) { // filter all ints bigger than 5
itr.remove();
}
}

当然,你可以模仿Guava或Apache的操作方法,通过引入一个新的接口Predicate,这可能是高级开发人员才会做的事,如下:

public interface Predicate<T> {
boolean test(T o);
}
public static <T> void filter(Collection<T> collection, Predicate<T> predicate) {
if ((collection != null) && (predicate != null)) {
Iterator<T> itr = collection.iterator();
while(itr.hasNext()) {
T obj = itr.next();
if (!predicate.test(obj)) {
itr.remove();
}
}
}
}

然后,我们使用如下代码去过滤集合:

filter(list, new Predicate<Integer>() {
public boolean test(Integer i) {
return i <= 5;
}
});

List转Set最简单的方法

有两种方法来实现该功能,取决于你如何定义“相等”。第一种方法将list存入HashSet,元素的重复主要由hashCode()来区分,大多数情况下,这是可行的。但是,如果你需要自己定义相等的比较方式,最好使用第二种方法,定义自己的比较器。

Set<Integer> set = new HashSet<Integer>(list);
Set<Integer> set = new TreeSet<Integer>(aComparator);
set.addAll(list);

ArrayList中删除重复元素

这个问题和上一个很像,如果你不关心ArrayList中元素的顺序的话,一个聪明的方法是通过将list元素存入set集合中来去除重复元素,然后将set集合的元素移回到List中。如下代码:

ArrayList** list = ... // initial a list with duplicate elements
Set<Integer> set = new HashSet<Integer>(list);
list.clear();
list.addAll(set);

如果你关心元素的顺序的话,可以使用标准JDK中的LinkedHashSet来实现该功能。

对集合排序

Java中有几种方式来维持集合中元素的顺序,它们提供了默认的排序顺序或者通过指定比较器来排序。不过即使是默认的排序,集合中的任何元素也需要实现Comparable接口。

  • Collections.sort()方法能够对一个List集合进行排序,正如javadoc中描述的,这种排序方法是稳定且能保证排序性能为n log(n)
  • PriorityQueue为一个有序队列,它与Collections.sort()的区别是PriorityQueue队列一直是有序的,但是你只能访问队头和队尾,不能随即访问元素,如PriorityQueue.get(4)之类的操作。
  • 如果集合中没有重复的元素,TreeSet是另外一种选择。跟PriorityQueue类似,它能一直维护元素的顺序,你能直接获取TreeSet中的第一个和最后一个元素,但是你仍然不能随即访问集合中的元素。

简单的说,Collections.sort()提供了对List的一次性排序,PriorityQueue和TreeSet能一直维持集合中元素的顺序,但是不能随即访问元素。

Collections.emptyList()与直接new一个实例的区别

该问题也适用于emptyMap()和emptySet()。

这两种方式都返回了一个空集合,但是Collections.emptyList()返回的是一个不可变集合,意味着你不能往这个空集合新增元素。事实上,每次调用Collections.emptyList()并不会创建一个空集合,而是复用已经存在的空集合实例。如果你熟悉单例模式的话,你应该知道我所说的,如果你频繁调用的话,这将会提供更好的性能。

Collections.copy方法

有两种方法讲一个List集合拷贝到另外一个List集合,其中一种是使用ArrayList的构造方法,如下:

ArrayList<Integer> dstList = new ArrayList<Integer>(srcList);

另一种是使用Collections.copy()方法(如下),注意第一行,我们分配了一个和源List集合长度相等的初始容量。

ArrayList<Integer> dstList = new ArrayList<Integer>(srcList.size());
Collections.copy(dstList, srcList);

这两种方法都使用浅拷贝,那么这两种方法的区别是什么呢?

  • 首先,当目标集合没有足够的空间存放源集合中的元素时,Collections.copy()方法不会对目标集合扩容,它会抛出一个IndexOutOfBoundsException异常。
  • Collections.copy()的参数类型只能是List接口的实现类,而ArrayList的构造方法可以接受Collection接口的实现类作为入参,因此更加普通。

译文链接:http://www.programcreek.com/2013/09/top-10-questions-for-java-collections/

Java集合的10个最常见问题的更多相关文章

  1. Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...

  2. Java 集合系列 10 Hashtable详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  3. Java集合框架 10 连问,你有被问过吗?

    首先要说一下,本文对这些Java集合框架的面试题只做了一个总结式的回答,对每一道题目,都值得深入去了解一下(什么是扎实基本功,这些就是基本功~~),后续可能对每一道题目拆开独立篇章来深入讲解一下. 大 ...

  4. Java集合学习(10):hashcode() 和 equals()方法

    哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个 ...

  5. 【转】Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...

  6. Java 集合系列目录(Category)

    下面是最近总结的Java集合(JDK1.6.0_45)相关文章的目录. 01. Java 集合系列01之 总体框架 02. Java 集合系列02之 Collection架构 03. Java 集合系 ...

  7. Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例

    概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...

  8. Java 集合系列18之 Iterator和Enumeration比较

    概要 这一章,我们对Iterator和Enumeration进行比较学习.内容包括:第1部分 Iterator和Enumeration区别第2部分 Iterator和Enumeration实例 转载请 ...

  9. Java 集合系列14之 Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)

    概要 学完了Map的全部内容,我们再回头开开Map的框架图. 本章内容包括:第1部分 Map概括第2部分 HashMap和Hashtable异同第3部分 HashMap和WeakHashMap异同 转 ...

随机推荐

  1. MDI窗体容器 权限设置

    MDI窗体容器:它可以让其它窗体在它的内部打开,无法超出它的范围将某个窗体的属性:IsMdiContainer设置为true - 窗口样式如何将其它窗体在它的内部打开?窗体对象名.MdiParent ...

  2. 关于ajax的同步和异步

    今天在开发中遇到个ajax访问Action 返回json 拼接Html,有时候能显示,有时候显示不出来. 考虑后一定是页面元素加载的问题. 所以把jq ajax设置为同步. 同步意为当访问后台代码时, ...

  3. Xcode开发工具问题

    昨天打开Xcode 发现鼠标光标变成了黑色的块状,不能编辑内容了,且Content区域还多出了一个显示文件路径的框框 如下图: 然后自己百度,到论坛提问.到QQ群里问;卸载重装Xcode.重装系统各种 ...

  4. C# POST Https请求的一些坑

    写在前面: 从上次,跟合作方的站点对接开始就产生了这个问题,当时用C#进行POST提交,总是会出现问题,找了很久发现对方的站点居然是TLS 1.2 的. 正文: 然而,在.NET FrameWork ...

  5. jquery基本选择器id

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. MSSQL存储过程返回自定义标识

    比如我们要做一个登陆,要求严格的也许要进行很多的判断, 如果这时不用自定义返回变量,就会多写很多的代码判断,多次操作数据库... if  exists(select * from sysyobject ...

  7. 说说Java生态圈的那些事儿

    文章目录: 1.生态圈概述. 2.说说Java,高级Java,Java生态圈的衍生 3. 说说servlet.servlet容器.比较tomcat.jetty.tomcat.jboss: 1. 哪个项 ...

  8. 回文字算法(java版本)

    package com.gdh.backtext;import java.util.HashMap;import java.util.Map;import java.util.Map.Entry; p ...

  9. PDF.NET SOD 开源框架红包派送活动 && 新手快速入门指引

    一.框架的由来  快速入门 有关框架的更多信息,请看框架官方主页! 本套框架的思想是借鉴Java平台的Hibernate 和 iBatis 而来,兼有ORM和SQL-MAP的特性,同时还参考了后来.N ...

  10. SQL查询数据的几大方法

    有你,查询数据我什么都不怕.快快掌握!! 出大招的工具: 1.使用LIKE.BETWEEN.IN进行模糊查询 eg1: SELECT * FROM Students WHERE 姓名 like '张% ...