java 8 流上的多个 .sorted(...) 调用

Ale*_*tig 5 java java-8 java-stream

我有一个对象流,我想对其进行自然排序,但也强制其中一个成员始终位于第一位。

例如:

List<String> result = Stream.of("a", "s", "d", "f")
        .sorted(Comparator.comparing((String s) -> !s.equals("d"))
                            .thenComparing(Comparator.naturalOrder()))
        .collect(toList());
System.out.println(result);
Run Code Online (Sandbox Code Playgroud)

这会产生

[d, a, f, s]
Run Code Online (Sandbox Code Playgroud)

也就是说,按字母顺序排列,但以“d”开头。

我注意到我还可以使用多个.sorted(...)调用来编写此内容:

List<String> result = Stream.of("a", "s", "d", "f")
        .sorted()
        .sorted(Comparator.comparing(s -> !s.equals("d")))
        .collect(toList());
System.out.println(result);
Run Code Online (Sandbox Code Playgroud)

这会产生相同的结果,并且在我看来更具可读性。

但是,我没有看到其他人这样做的例子。

此外,IntelliJ IDEA 将第一次.sorted()调用标记为冗余。它说“随后的‘排序’调用使排序变得毫无用处”。

这显然是不正确的,因为删除调用会将输出更改为

[d, a, s, f]
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:.sorted(...)对 Stream 的多次调用是否定义了行为,还是我只是运气好?

换句话说,这是.sorted()我可以依赖的受支持的使用,还是它只是现在才起作用,并且可能在将来的某个 Java 版本中停止工作?

Ous*_* D. 4

当你说“定义”时,我假设你在问“排序是否稳定”?如果是这样,那么是的。

第二个示例片段的第一次sorted()调用将根据自然顺序对元素进行排序,然后根据提供的比较器再次排序。

第二种方法的问题是效率低下,因为它需要两次排序操作,因此人们可能会决定继续使用第一种方法。

  • @AlexWittig在某些情况下,流可以利用它们对源和先前操作的了解来完全省略操作。但在这种特殊情况下 `sorted()` 和 `.sorted(Comparator.comparing(s -&gt; !s.equals("d")))` 是两个不同的东西,因为前者按自然顺序排序,而后者按自然顺序排序提供的比较器。我不希望任何未来的版本会省略第一个“已排序”调用。如果我们有 `.sorted().sorted()` 那么省略对 `sorted` 的第二次调用会更有意义。 (2认同)
  • @AlexWittig通过省略“排序”操作来优化流管道的唯一情况是,如果已知元素已经按自然顺序排序,并且您想要链接“另一个”自然顺序排序,因此后续`sorted` 可以成为空操作。也就是说,您可能会发现[这篇文章](https://www.ibm.com/developerworks/library/j-java-streams-3-brian-goetz/index.html)关于标志以及有时一些中间操作如何您感兴趣的内容将被完全忽略。 (2认同)