使用distinct()和collect(toSet())是否值得

时间:2017-01-11 14:31:30

标签: java java-stream

在将流的元素收集到集合中时,在流上指定.distinct()是否有任何优势(或缺点)?例如:

return items.stream().map(...).distinct().collect(toSet());

鉴于该集合已经删除重复项,这似乎是多余的,但它是否提供任何性能优势或劣势?答案取决于流是并行/顺序还是有序/无序?

3 个答案:

答案 0 :(得分:8)

根据javadocdistinct是一个有状态的中间操作。

如果您确实.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)等等。
  • 使用distinct()可以保留此信息,例如:(b, a)

然而,这假设了一些先决条件(顺序等)。

注意:这可以使用TreeSet和适当的compareTo方法完成。

答案 2 :(得分:0)

distinct会调用equals / hashcode来分隔项目,稍后toSet会做同样的事情(即使在不需要之后,但是toSet也不能真正知道),所以基本上你只是重复调用。它应该表现稍差IMO。测量也很容易。