Java集合 - Set.add() 和 Set.addAll() 哪个更快?

5

Set维护唯一记录,并在尝试重复现有元素时更新现有记录。

考虑以下两种情况。你认为哪一个代码更快、更有效率?

方案1:使用addAll()

Set<String> uniqueSet = new HashSet<String>();
uniqueSet = getSomedata(param1);
uniqueSet.addAll( getSomedata(param2) );

这里的getSomedata()方法只是返回一组数据,没有任何特殊逻辑。

场景2:使用add()方法

Set<String> uniqueSet = new HashSet<String>();
getSomedata(param1, uniqueSet);
getSomedata(param2, uniqueSet );

这里的getSomedata()如下所示:
void getSomedata(String param, Set<String> uniqueSet){
    while (someCollection.hasNext()){
        uniqueSet.add( someCollection.get() );
    }
}

1
看一下实现,它在随JDK提供的src.zip中。如果你正确地设置了你的IDE,你应该能够在那里查看它。 - the8472
2
首先,第一个代码片段不应该创建一个无用的空HashSet。其次,你应该追求的不是性能。这两者之间的差别可能微不足道。你应该追求的是可读性和可维护性。我期望一个名为getSomedata()的方法返回一些数据。而不是接受一个Set作为参数,填充它,并返回什么都没有。如果你想将数据添加到List而不是Set中怎么办?或者如果你只想遍历它呢?第一个选项更自然、更易于理解和使用。 - JB Nizet
@JBNizet,实际上在我的应用程序中,我正在从服务器暴露的文件中读取大量数据。文件内的行是唯一的,但可以在多个文件中重复。在收集所有文件的数据之后,我只需要处理唯一的记录。正如您所知,List不强制唯一性。因此,我选择使用Set。 - sabtharishi
1
在IO操作中花费的时间可能比处理集合所花费的时间要大得多。使用您认为最易读的方式,并仅在必要时进行优化。 - JB Nizet
2个回答

2

你的问题不完整。让我们用实际的替代方案来完成它。

首先,你有一个方法,它填充了一个提供的Set

void getSomedata(String param, Set<String> uniqueSet)

这需要像以下方式使用:

Set<String> uniqueSet = new HashSet<String>();
getSomedata(param1, uniqueSet);
getSomedata(param2, uniqueSet);

另一种方法是返回一个新的Set

Set<String> getSomedata(String param)

您可以像使用以下方式一样使用:

Set<String> uniqueSet = getSomedata(param1);
uniqueSet.addAll( getSomedata(param2) );

在这种情况下,您忽略了方法getSomedata如何创建和填充它将返回的Set。显然,除非它创建一个定制的Set实现以投影源数据,否则它必须创建一个Set并填充它以在返回之前返回它。
换句话说,在您调用addAll时,无论它是如何实现的,此解决方案都已执行与其他替代方案相同的工作,因为它已将所有元素添加到了Set中。因此,即使特定Set实现的addAll具有优化,它的工作也会增加到单独将所有元素添加到Set中已经执行的工作中。
尽管如此,除非存在真正的性能问题,否则不必担心性能问题。涉及到的I/O可能超过所有内容。或热点优化和内存管理的不可预测性可能会改变一切。如果您认为返回新的Set更清晰(那是合理的),请使用它。
作为补充,我简化了一些内容。 HashSet仅在理论上为O(1),但在哈希冲突和使用TreeSet的情况下,其时间复杂度为O(log n),集合大小不同会产生影响,因此,基于不同大小的集合的替代方案无法直接比较,这取决于使用的Set实现和其他周围上下文。但趋势仍然是相同的,特别是在大多数情况下,没有优化的addAll实现(EnumSet可能是唯一的例外)。

2

addAll 基本上会遍历给定的集合,并在每个元素上调用 add 方法。以下是 OpenJDK8 的实现方式:

public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

但通常而言,除非你确信能够发明出更好的轮子,否则不应该试图重新发明轮子。


仅供参考,JDK 7是相同的。 - vikingsteve
据我所知,这个问题并不是关于addAll()和add()的区别,而是关于每个方法调用创建自己的小集合,然后将它们全部添加到一个唯一的大集合中,与创建一个唯一的集合并让方法将数据添加到这个唯一的集合中的区别。 - JB Nizet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接