在将流的元素收集到集合中时,在流上指定.distinct()
是否有任何优势(或缺点)?例如:
return items.stream().map(...).distinct().collect(toSet());
鉴于该集合已经删除重复项,这似乎是多余的,但它是否提供任何性能优势或劣势?答案取决于流是并行/顺序还是有序/无序?
答案 0 :(得分:8)
根据javadoc,distinct
是一个有状态的中间操作。
如果您确实.distinct
紧跟.collect
,那么它并没有真正增加任何好处。也许如果.distinct
实施比Set
重复检查更有效,那么您可能会获得一些好处,但是如果您正在收集到一套,那么您将最终获得同样的结果。
另一方面,如果在.distinct
操作之前发生.map
,并且该特定映射是一项昂贵的操作,那么您可能会获得一些收益,因为您整体处理的数据较少
答案 1 :(得分:1)
虽然你得到了相同的结果,但他们不会做同样的事情:toSet()
使用HashSet
,你就失去了初始排序,如果需要,可以保留不同的内容:
来自javadoc:
保持并行管道中distinct()的稳定性是 相对昂贵(要求操作充分 屏障,具有大量缓冲开销),并且通常是稳定的 不需要。使用无序流源(例如 生成(供应商))或删除订购约束 BaseStream.unordered()可能会显着提高效率 并行管道中执行distinct(),如果是语义 你的情况许可。如果与遭遇顺序的一致性是 需要,并且您遇到性能或内存不佳 并行管道中使用distinct(),切换到 使用BaseStream.sequential()的顺序执行可能会改进 性能
如果您需要稳定性,则为distinct()
。之后使用toSet()
将毫无用处(如果API不需要)。
如果你有equals
实现部分相等,那么这很有用:
class F {
int a;
int b;
@Override int hashCode() {return Objects.hashCode(a);}
@Override boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof F)) return false;
return a == ((F)other).a;
}
}
如果您有a = F(10, 1)
和b = F(10, 2)
则等于。但并非所有领域都是平等的。
如果在列表中您有(b, a)
toSet()
,您不会总是拥有此订单。你可能有(b,a)等等。(b, a)
。然而,这假设了一些先决条件(顺序等)。
注意:这可以使用TreeSet
和适当的compareTo
方法完成。
答案 2 :(得分:0)
distinct会调用equals / hashcode来分隔项目,稍后toSet会做同样的事情(即使在不需要之后,但是toSet也不能真正知道),所以基本上你只是重复调用。它应该表现稍差IMO。测量也很容易。