本文是
《关于 Java 集合的十大问题》一文的翻译。以下是 Stackowerflow 上有关 Java 集合的最常见问题。在看这些问题之前,最好先看一下类层次结构图。
1.什么时候使用LinkedList代替ArrayList?
ArrayList实际上是一个数组;它的元素可以通过索引直接访问。如果数组溢出,则需要一个具有更多空间的新数组。放置和移动所有元素将花费 O(n) 时间。此外,添加和删除元素对于移动数组中的现有元素是必要的。这也许是使用ArrayList最大的不便之处。LinkedList 是元素链接的双重列表。因此,要访问中心的元素,您必须从工作表的开头到结尾进行搜索。另一方面,在 LinkedList 中添加和删除元素速度更快,因为这些操作只会更改列表本身。最糟糕的时间比较如下:
方法 |
数组列表 |
链表 |
获取(索引) |
复杂度(1) |
在) |
添加(E) |
在) |
复杂度(1) |
添加(E,索引) |
在) |
在) |
删除(索引) |
在) |
在) |
迭代器.remove() |
在) |
复杂度(1) |
迭代器.add(E) |
在) |
复杂度(1) |
尽管运行时间较长,但对于大型列表,必须单独考虑内存使用情况。在 LinkedList 中,每个节点必须至少有两个附加指针来链接前一个和下一个节点,而在 ArrayList 中,只需要一个元素数组。更多ArrayList、LinkedList和Vector
列表的比较。
2. 在集合迭代期间删除元素的高效等效方法 在迭代期间修改(删除元素)集合的唯一正确方法是使用
Iterator.remove()。例如: 最常见的错误是: 运行上面的代码时, 您将收到
ConcurrentModificationException 。发生这种情况是因为生成迭代器是为了在整个列表中移动,但同时通过调用 Iterator.remove() 更改了工作表。正如该异常的文档中所写,
Iterator
itr = list.iterator(); while(itr.hasNext()) { // do something itr.remove(); }
for(Integer i: list) { list.remove(i); }
“通常不允许一个线程修改集合,而另一个线程正在迭代它。”
一般来说,一个线程修改集合而另一个线程正在遍历它是不可接受的。
3. 如何将List转换为int[]数组? 最简单的方法是使用位于
Apache Commons Lang库中的
ArrayUtils。 JDK 中没有此表达式的快捷方式。请记住,您不能使用 List.toArray(),因为此表达式将 List 转换为 Integer[](这
不是基本类型)。正确的做法是以下选项:
4. 如何将 int[] 数组转换为 List? 最简单的方法也是使用
Apache Commons Lang库中的
ArrayUtils,如上所述。 另外,JDK 中没有此表达式的快捷方式。
5. 过滤集合的最佳方式是什么? 您可以使用第三方软件包(例如
Guava或
Apache Commons Lang)来增加功能。这两个包都有一个filter()方法(在Guava的
Collections2类和Apache的
CollectionUtils中)。filter() 方法将返回与给定谓词匹配的元素。在 JDK 中,一切都更加复杂。好消息是 Java 8 中将添加谓词
,但现在您需要使用 Iterator 来迭代整个集合。 当然,您可以通过熟悉新的 Predicate 接口来模仿 Guava 和 Apache 所遵循的路径。 现在我们可以使用下面的代码来过滤集合:
6. 如何轻松地将List转换为Set? 有两种方法可以做到这一点,具体取决于您想要如何定义平等。第一段代码将列表放入 HashSet 中。这种情况下的重复主要是通过hashCode()来确定的。通常这会起作用。但如果您需要考虑比较路径,那么最好使用代码的第二部分,您可以在其中定义自己的比较器。
7. 如何从ArrayList中删除重复元素? 这个问题和上面的问题有些关联。如果 ArrayList 中元素的顺序对您来说并不重要,那么明智的做法是将工作表放入 Set 中以删除重复项,然后将其返回到 List。下面是一个例子。 如果元素的顺序对您很重要,那么可以通过将列表放置在标准 JDK 中的
LinkedHashSet中来确保顺序。
8. 排序集合
int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0]));
int[] array = new int[list.size()]; for(int i=0; i < list.size(); i++) { array[i] = list.get(i); }
List list = Arrays.asList(ArrayUtils.toObject(array));
int[] array = {1,2,3,4,5}; List
list = new ArrayList
(); for(int i: array) { list.add(i); }
Iterator
itr = list.iterator(); while(itr.hasNext()) { int i = itr.next(); if (i > 5) { // filter all ints bigger than 5 itr.remove(); } }
public interface Predicate
{ boolean test(T o); } public static
void filter(Collection
collection, Predicate
predicate) { if ((collection != null) && (predicate != null)) { Iterator
itr = collection.iterator(); while(itr.hasNext()) { T obj = itr.next(); if (!predicate.test(obj)) { itr.remove(); } } } }
filter(list, new Predicate
() { public boolean test(Integer i) { return i <= 5; } });
Set
set = new HashSet
(list);
Set
set = new TreeSet
(aComparator); set.addAll(list);
ArrayList** list = ... // initial a list with duplicate elements Set
set = new HashSet
(list); list.clear(); list.addAll(set);
Java 中支持排序集合的方法有多种。它们都按自然顺序或按指定的比较器提供集合。在自然顺序的情况下,还需要在元素上 实现
Comparable接口。
- Collections.sort()可以对List进行排序。正如 Java 文档中所述,这种排序是稳定的并保证 n log(n) 性能。
- PriorityQueue提供了一个有序的队列。PriorityQueue 和 Collections.sort() 的区别在于,PriorityQueue 始终保持队列的顺序,但只能获取队列的第一个元素。您不能随机访问像 PriorityQueue.get(4) 这样的元素。
- 如果集合中没有重复项,您可以选择TreeSet。与 PriorityQueue 一样,TreeSet 始终维护有序集。您可以从 TreeSet 中获取最小或最大的元素,但仍然无法随机访问这些元素。
简单地说,Collections.sort() 提供了一个一次性排序列表。PriorityQueue 和 TreeSet 始终维护有序集合,但代价是缺乏对元素的索引访问。
9. Collections.emptyList() 或新实例 同样的问题也适用于emptyMap() 和emptySet()。两种方法都返回一个空列表,但 Collections.emptyList() 是一个不可变列表。这意味着您
无法将新元素添加到“空”列表中。在后台,每次调用 Collections.emptyList() 方法实际上并不创建空列表的新实例。相反,它将重用已经存在的空实例。如果您熟悉 Singleton作为一种
设计模式,您应该明白其含义。如果频繁调用, 这应该会给您带来
更好的性能。
10 复制集合,Collections.copy() 有两种方法可以将源列表复制到目标列表。一种方法是使用 ArrayList 构造函数。 另一种方法是使用
Collections.copy()方法。注意第一行:我们分配的列表的长度至少与原始列表的长度相同,因为关于集合的 Java 文档说:
ArrayList
dstList = new ArrayList
(srcList);
目标列表必须至少与源列表一样长。
这意味着最终列表不得短于原始列表。 这两种方法都是浅复制。那么这两种方法有什么区别呢?首先,Collections.copy() 不会重新分配 dstList 集合的容量,即使 dstList 没有足够的空间来包含 srcList 中的所有元素。相反,它会抛出
IndexOutOfBoundsException。有人可能会问这样做有什么好处吗?原因是这确保了该方法在时间上线性运行。当您想要重用数组而不是在 ArrayList 构造函数中重新分配内存时,这也适用。
而不是结论 如果读完文章后您仍然有疑问,请随时在评论中提问。另外,如果您发现翻译有任何不准确或任何其他错误,请写信给PM,我们会更正,我们将感谢您。
原来的。
ArrayList
dstList = new ArrayList
(srcList.size()); Collections.copy(dstList, srcList);
GO TO FULL VERSION