307. Java Stream API - 排序流
在 Java 中,SORTED 流表示元素已经按某种规则排序。这些流可以通过两种方式创建:
- 排序的源:例如
TreeSet,它自然保持元素的顺序。 - 调用
sorted()方法:该方法可以将流中的元素按自然顺序或自定义的比较器进行排序。
知道流是已排序的,可以帮助流的实现做一些优化。例如,当流已经是排序好的时,流的实现可以跳过再次排序的步骤,从而提高效率。然而,流的排序特性并非在所有情况下都能被优化。因为有时候,如果使用不同的比较器对流进行排序,排序特性可能会丧失。
✅ SORTED 特性的清除
某些中间操作会移除流的 SORTED 特性。例如,当你使用 map() 或 flatMap() 等操作时,即使流之前是排序过的,结果流将不再被视为 SORTED 流。
示例:检查一个流是否是 SORTED 流
你可以通过 Spliterator 接口中的 characteristics() 方法来检查流是否具有 SORTED 特性。下面是一个简单的例子,展示如何检查流的排序特性:
Predicate<Stream<?>> isSorted =
stream -> ((stream.spliterator().characteristics() & Spliterator.SORTED) != 0);
List<String> strings = List.of("one", "two", "two", "three", "four", "five");
System.out.println("Is strings sorted? " + isSorted.test(strings.stream())); // false
Stream<String> sortedStrings = strings.stream().sorted();
System.out.println("Is sortedStrings sorted? " + isSorted.test(sortedStrings)); // true
Stream<String> filtered = strings.stream().sorted().filter(s -> s.length() < 5);
System.out.println("Is filtered sorted? " + isSorted.test(filtered)); // true
Stream<Integer> lengths = strings.stream().sorted().filter(s -> s.length() < 5).map(String::length);
System.out.println("Is lengths sorted? " + isSorted.test(lengths)); // false
解释:
strings.stream()返回的流没有排序,所以isSorted.test(strings.stream())返回false。sortedStrings是通过调用sorted()创建的流,它是有序的,因此isSorted.test(sortedStrings)返回true。filtered流是对sortedStrings进行过滤后的结果,它依然保持排序特性,因为过滤操作不会影响排序。lengths流通过map()转换了元素(将字符串转换为其长度),这会导致移除SORTED特性,因此isSorted.test(lengths)返回false。
输出:
Is strings sorted? false
Is sortedStrings sorted? true
Is filtered sorted? true
Is lengths sorted? false
✅ 排序流的创建
通过 sorted() 创建排序流
sorted() 方法可以创建一个有序的流,默认使用元素的自然顺序进行排序,也可以通过传入一个自定义的比较器来指定排序方式:
示例 1:使用自然顺序排序
Stream<String> sortedStream = Stream.of("one", "two", "three", "four").sorted();
sortedStream.forEach(System.out::println);
输出:
four
one
three
two
示例 2:使用自定义比较器进行排序
Stream<String> sortedStream = Stream.of("one", "two", "three", "four")
.sorted(Comparator.comparingInt(String::length)); // 按字符串长度排序
sortedStream.forEach(System.out::println);
输出:
one
two
four
three
通过 TreeSet 创建排序流
TreeSet 是一种排序的集合,它会自动根据元素的自然顺序进行排序,或者你可以提供一个自定义的比较器:
Set<String> sortedSet = new TreeSet<>(Comparator.comparingInt(String::length));
sortedSet.addAll(List.of("one", "two", "three", "four"));
sortedSet.forEach(System.out::println);
输出:
one
two
four
three
✅ 流的排序特性清除
某些操作会移除流的 SORTED 特性,特别是当你进行元素转换或映射时。例如,map() 和 flatMap() 操作都会导致排序特性丧失。
示例:排序特性丧失
Stream<String> sortedStream = Stream.of("apple", "banana", "cherry").sorted();
Stream<Integer> mappedStream = sortedStream.map(String::length); // map 操作移除了排序特性
boolean isSortedAfterMap = isSorted.test(mappedStream);
System.out.println("Is mappedStream sorted? " + isSortedAfterMap); // false
输出:
Is mappedStream sorted? false
通过 map() 操作,排序特性被移除,因为 map() 可能会修改元素的顺序。
✅ 总结
- 排序流是具有
SORTED特性的流,它可以通过TreeSet等有序数据源或调用sorted()方法创建。 - 一些中间操作,如
map()或flatMap(),会移除SORTED特性。 - 知道一个流是否已排序,能够帮助我们做出更高效的并行流处理决策,特别是在需要避免不必要的排序时。
通过掌握如何使用和检查流的排序特性,可以让我们编写更加高效的流操作,避免不必要的性能开销。