【问题标题】:What is the best way to aggregate Streams into one DISTINCT with Java 8使用 Java 8 将 Streams 聚合为一个 DISTINCT 的最佳方法是什么
【发布时间】:2016-05-15 08:31:12
【问题描述】:

假设我有多个 java 8 流,每个流可能都可以转换为 Set<AppStory> ,现在我希望以最佳性能将所有流按 ID 聚合到一个 DISTINCT 流中,按属性排序(“lastUpdate”)

有几种方法可以做,但我想要最快的一种,例如:

Set<AppStory> appStr1 =StreamSupport.stream(splititerato1, true).
map(storyId1 -> vertexToStory1(storyId1).collect(toSet());

Set<AppStory> appStr2 =StreamSupport.stream(splititerato2, true).
map(storyId2 -> vertexToStory2(storyId1).collect(toSet());

Set<AppStory> appStr3 =StreamSupport.stream(splititerato3, true).
map(storyId3 -> vertexToStory3(storyId3).collect(toSet());


Set<AppStory> set = new HashSet<>();
set.addAll(appStr1)
set.addAll(appStr2)
set.addAll(appStr3) , and than make sort by "lastUpdate"..

//POJO Object:
public class AppStory implements Comparable<AppStory> {
private String storyId;
private String ........... many other attributes......
public String getStoryId() {
    return storyId;
}
@Override
public int compareTo(AppStory o) {
    return this.getStoryId().compareTo(o.getStoryId());
   }
}

...但这是旧方法。

如何创建一个 DISTINCT 按 ID 排序的流并具有最佳性能

有点像:

  Set<AppStory> finalSet = distinctStream.sort((v1, v2) -> Integer.compare('not my issue').collect(toSet())

有什么想法吗?

BR

活力

【问题讨论】:

  • 您的equals 方法看起来如何?
  • @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) 返回 false; AppStory appStory = (AppStory) o; return !(storyId != null ? !storyId.equals(appStory.storyId) : appStory.storyId != null); }
  • 我认为类似: Set dsd = Stream.of(appStr1, appStr2).flatMap(Stream::distinct).sorted((s1, s2) -> Long.compare(s1 .getLastUpdateTime(), s2.getLastUpdateTime())).collect(toSet());
  • 每个Spliterator 有多少个元素,vertexToStory 方法是否昂贵?
  • 每个Spliterator大约有1000个元素,vertexToStory方法将DB属性转换为POJO,-不贵

标签: java java-8 java-stream


【解决方案1】:

我认为并行开销远大于您在 cmets 中所述的实际工作。因此,让您的Streams 按顺序完成这项工作。

仅供参考:您应该更喜欢使用Stream::concat,因为Stream::limit 之类的切片操作可以被Stream::flatMap 绕过。

Stream::sortedStream 中的每个元素收集到List 中,对List 进行排序,然后将元素按所需顺序推送到管道中。然后再次收集元素。因此,可以通过将元素收集到 List 并在之后进行排序来避免这种情况。使用List 是比使用Set 更好的选择,因为顺序很重要(我知道有LinkedHashSet,但您无法对其进行排序)。

在我看来,这是最干净,也许是最快的解决方案,因为我们无法证明它。

Stream<AppStory> appStr1 =StreamSupport.stream(splititerato1, false)
                                       .map(this::vertexToStory1);
Stream<AppStory> appStr2 =StreamSupport.stream(splititerato2, false)
                                       .map(this::vertexToStory2);
Stream<AppStory> appStr3 =StreamSupport.stream(splititerato3, false)
                                       .map(this::vertexToStory3);

List<AppStory> stories = Stream.concat(Stream.concat(appStr1, appStr2), appStr3)
                               .distinct().collect(Collectors.toList());
// assuming AppStory::getLastUpdateTime is of type `long`
stories.sort(Comparator.comparingLong(AppStory::getLastUpdateTime));

【讨论】:

  • 并行 distinct 对于 ordered 流来说是昂贵的,但对于无序流来说,效率要高得多。 (在有序流中,distinct() 必须保留 first 的相等出现,类似于排序中的稳定性要求。)将unordered() 放入流中,您可能会发现并行性能更具吸引力。
  • 使用Stream.toArray(),后接Arrays.sortArrays.parallelSort,再接Arrays.asList,您可能会获得更好的整体性能。
【解决方案2】:

我不能保证这会比你拥有的更快(我猜是这样,但你必须测量以确保),但你可以简单地这样做,假设你有 3 个流:

List<AppStory> distinctSortedAppStories = 
    Stream.of(stream1, stream2, stream3)
          .flatMap(Function.identity())
          .map(this::vertexToStory)
          .distinct()
          .sorted(Comparator.comparing(AppStory::getLastUpdate))
          .collect(Collectors.toList());

【讨论】:

  • 你为什么使用 "..map(this::vertexToStory)" ?所有流 "stream1, stream2, stream3" ...已经通过 map 函数进行了转换。为什么不使用 Stream::distinct ?
  • 因为它允许制作一次而不是三次:只需传递原始流而不是传递 3 个“映射”流,然后让组合流进行映射。关于第二部分:组合 3 个不同的流不会导致不同的流。您需要在组合流上使用 distinct(),以确保所有元素都是唯一的。
  • 也许你可以用 Stream.concat 代替 stream.of + flatmqp
  • 我不确定哪个性能更好,但是当我看到 distinct + sorted 时,我通常会考虑将两者都替换为对 TreeSet 的收集。
  • @HankD 但是区分和排序是基于不同的字段。所以你不能使用TreeSet
猜你喜欢
  • 1970-01-01
  • 2023-01-15
  • 1970-01-01
  • 2017-10-17
  • 2010-12-23
  • 2018-07-16
  • 1970-01-01
  • 2019-10-03
相关资源
最近更新 更多