在 Java Stream API 中,sorted 是用于对数据流进行排序的中间操作,它会返回一个新的有序流(元素按指定规则排序),且不会修改原始集合。sorted 操作有两种重载形式,分别支持自然排序和自定义排序,适用于需要对元素顺序进行调整的场景(如按数值大小、字符串长度、对象属性等排序)。
一、sorted 操作的两种形式
1. 自然排序:sorted()
- 适用场景:流中的元素类型实现了
java.lang.Comparable接口(如Integer、String、LocalDate等),可直接按其默认规则排序。 - 原理:调用元素自身的
compareTo方法进行比较(例如Integer按数值大小,String按字典序)。
示例:对整数流进行自然排序(升序)
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5);
numbers.stream()
.sorted() // 自然排序(Integer实现了Comparable)
.forEach(System.out::print); // 输出:1 1 3 4 5
示例:对字符串流进行自然排序(字典序)
List<String> words = Arrays.asList("banana", "apple", "cherry");
words.stream()
.sorted() // 按字符串字典序(A-Z)
.forEach(System.out::println);
// 输出:
// apple
// banana
// cherry
2. 自定义排序:sorted(Comparator<T> comparator)
- 适用场景:元素未实现
Comparable,或需要按自定义规则排序(如降序、按对象属性排序等)。 - 原理:通过传入
java.util.Comparator接口的实现类(通常用 Lambda 表达式或方法引用简化),定义元素的比较规则。
示例 1:按整数降序排序
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5);
numbers.stream()
.sorted((a, b) -> b.compareTo(a)) // 自定义比较器(降序)
.forEach(System.out::print); // 输出:5 4 3 1 1
也可使用 Comparator 工具类简化:
import static java.util.Comparator.reverseOrder;
numbers.stream()
.sorted(reverseOrder()) // 等价于上述降序逻辑
.forEach(System.out::print);
示例 2:按字符串长度排序(短→长)
List<String> words = Arrays.asList("banana", "apple", "cherry", "a");
words.stream()
.sorted((s1, s2) -> Integer.compare(s1.length(), s2.length())) // 按长度比较
.forEach(System.out::println);
// 输出:
// a
// apple(5)
// banana(6)
// cherry(6)
使用方法引用进一步简化:
import static java.util.Comparator.comparingInt;
words.stream()
.sorted(comparingInt(String::length)) // 按长度排序(Comparator工具类)
.forEach(System.out::println);
示例 3:按对象属性排序
假设有 User 类(含 age 属性),需按年龄升序排序:
class User {
private String name;
private int age;
// 构造器、getter省略
public User(String name, int age) {
this.name = name; this.age = age; }
public int getAge() {
return age; }
public String getName() {
return name; }
}
List<User> users = Arrays.asList(
new User("Bob", 20),
new User("Alice", 18),
new User("Charlie", 25)
);
// 按年龄升序排序
users.stream()
.sorted(comparingInt(User::getAge)) // 引用User的age属性
.forEach(user -> System.out.println(user.getName() + "(" + user.getAge() + ")"));
// 输出:
// Alice(18)
// Bob(20)
// Charlie(25)
二、sorted 操作的关键特性
中间操作,惰性求值
sorted本身不会执行排序,仅在终端操作(如forEach、collect)触发时才会执行排序逻辑。稳定性
对于并行流,sorted保证排序结果与串行流一致(即稳定排序),但排序过程可能利用多线程加速。有序性(Ordering)
- 若原始流是有序的(如
List的流),sorted会返回一个新的有序流(排序后的顺序)。 - 若原始流是无序的(如
HashSet的流),sorted会强制返回有序流(排序后的顺序)。 - 注意:并行流的
sorted操作可能比串行流更耗时(因排序本身是CPU密集型,且并行需额外的线程协调)。
- 若原始流是有序的(如
不修改原始集合
sorted仅对“流中的元素”进行排序,原始集合的元素顺序不会改变。
三、使用注意事项
避免对大集合频繁排序
排序的时间复杂度通常为O(n log n),对超大集合(如百万级元素)使用sorted可能影响性能,需评估是否必要。Comparator的实现需满足规范
自定义比较器时,需保证compare(a, b)满足:- 自反性:
compare(a, a) = 0 - 对称性:
compare(a, b) = -compare(b, a) - 传递性:若
compare(a, b) > 0且compare(b, c) > 0,则compare(a, c) > 0
否则可能导致排序结果异常(如NullPointerException或不稳定排序)。
- 自反性:
处理
null元素
若流中包含null元素,自然排序(Comparable)会抛出NullPointerException,需在自定义比较器中显式处理null(例如将null排在最前或最后):List<String> words = Arrays.asList("banana", null, "apple"); words.stream() .sorted((s1, s2) -> { if (s1 == s2) return 0; if (s1 == null) return -1; // null排在前面 if (s2 == null) return 1; return s1.compareTo(s2); }) .forEach(System.out::println); // 输出: // null // apple // banana
四、总结
sorted 操作是 Stream API 中用于排序的核心工具,通过两种形式支持灵活的排序需求:
sorted():依赖元素的Comparable接口,实现自然排序;sorted(Comparator):通过自定义比较器,实现按任意规则排序(如降序、属性排序等)。
使用时需注意其惰性求值特性、有序性影响及性能开销,合理结合其他流操作(如 filter、limit)可高效处理排序后的数据流。