JavaRush /Java 博客 /Random-ZH /关于 Java 集合的十大问题
FedoraLinux
第 21 级
Москва

关于 Java 集合的十大问题

已在 Random-ZH 群组中发布
本文是关于 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. 过滤集合的最佳方式是什么? 您可以使用第三方软件包(例如 GuavaApache 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接口。
  1. Collections.sort()可以对List进行排序。正如 Java 文档中所述,这种排序是稳定的并保证 n log(n) 性能。
  2. PriorityQueue提供了一个有序的队列。PriorityQueue 和 Collections.sort() 的区别在于,PriorityQueue 始终保持队列的顺序,但只能获取队列的第一个元素。您不能随机访问像 PriorityQueue.get(4) 这样的元素。
  3. 如果集合中没有重复项,您可以选择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);
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION