Java?Stream?中的sorted?方法實現(xiàn)自定義排序全面解析(推薦)
Java Stream API 中的 sorted() 方法是一個強大的中間操作,它允許我們對流中的元素進(jìn)行排序。默認(rèn)情況下,sorted() 要求元素實現(xiàn) Comparable 接口,但在實際應(yīng)用中,我們經(jīng)常需要根據(jù)特定業(yè)務(wù)規(guī)則進(jìn)行自定義排序。本文將深入探討如何使用 sorted() 方法實現(xiàn)自定義排序,涵蓋各種常見場景和高級技巧。
一、sorted 方法基礎(chǔ)
Java Stream 提供了兩種 sorted() 方法重載:
自然排序:要求元素實現(xiàn) Comparable 接口
Stream<T> sorted()
自定義排序:通過 Comparator 指定排序規(guī)則
Stream<T> sorted(Comparator<? super T> comparator)
二、自定義排序的基本實現(xiàn)
1. 使用 Lambda 表達(dá)式創(chuàng)建 Comparator
// 示例1:按字符串長度排序
List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
List<String> sortedByLength = words.stream()
.sorted((s1, s2) -> s1.length() - s2.length())
.toList();
System.out.println(sortedByLength); // 輸出:[date, apple, cherry, banana]
// 示例2:按絕對值大小排序
List<Integer> numbers = Arrays.asList(-5, 2, -8, 1, 3);
List<Integer> sortedByAbs = numbers.stream()
.sorted((n1, n2) -> Math.abs(n1) - Math.abs(n2))
.toList();
System.out.println(sortedByAbs); // 輸出:[1, 2, 3, -5, -8]2. 使用 Comparator 靜態(tài)方法簡化代碼
Java 8 為 Comparator 接口提供了許多實用的靜態(tài)方法,使排序代碼更加簡潔:
// 使用 Comparator.comparing 方法
List<String> sortedByLength2 = words.stream()
.sorted(Comparator.comparing(String::length))
.toList();
// 使用 Comparator.comparingInt 優(yōu)化基本類型比較
List<Integer> sortedByAbs2 = numbers.stream()
.sorted(Comparator.comparingInt(Math::abs))
.toList();三、處理復(fù)雜對象排序
1. 對自定義對象按屬性排序
class Person {
private String name;
private int age;
private LocalDate birthDate;
// 構(gòu)造方法、getter和setter略
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + ", birthDate=" + birthDate + "}";
}
}
// 按年齡升序排序
List<Person> people = Arrays.asList(
new Person("Alice", 25, LocalDate.of(2000, 1, 1)),
new Person("Bob", 20, LocalDate.of(2005, 5, 5)),
new Person("Charlie", 30, LocalDate.of(1995, 10, 10))
);
List<Person> sortedByAge = people.stream()
.sorted(Comparator.comparingInt(Person::getAge))
.toList();
System.out.println(sortedByAge);
// 輸出:[Person{name='Bob', age=20}, Person{name='Alice', age=25}, Person{name='Charlie', age=30}]2. 多條件排序(復(fù)合排序)
使用 thenComparing() 方法可以實現(xiàn)多級排序:
// 先按年齡升序,年齡相同則按出生日期降序
List<Person> sortedByAgeAndBirthDate = people.stream()
.sorted(Comparator.comparingInt(Person::getAge)
.thenComparing(Person::getBirthDate, Comparator.reverseOrder()))
.toList();
System.out.println(sortedByAgeAndBirthDate);四、處理空值與 null 安全排序
1. 空值處理策略
在實際應(yīng)用中,集合元素或元素屬性可能為 null,直接排序會導(dǎo)致 NullPointerException。我們可以使用 Comparator.nullsFirst() 或 Comparator.nullsLast() 來安全處理 null 值:
// 示例:處理可能為 null 的字符串
List<String> stringsWithNulls = Arrays.asList("apple", null, "banana", null, "cherry");
// null 值排在前面
List<String> sortedWithNullsFirst = stringsWithNulls.stream()
.sorted(Comparator.nullsFirst(Comparator.naturalOrder()))
.toList();
System.out.println(sortedWithNullsFirst);
// 輸出:[null, null, apple, banana, cherry]
// null 值排在后面
List<String> sortedWithNullsLast = stringsWithNulls.stream()
.sorted(Comparator.nullsLast(Comparator.naturalOrder()))
.toList();
System.out.println(sortedWithNullsLast);
// 輸出:[apple, banana, cherry, null, null]2. 對象屬性可能為 null 的情況
class Product {
private String name;
private Double price; // 價格可能為 null
// 構(gòu)造方法、getter和setter略
}
List<Product> products = Arrays.asList(
new Product("Laptop", 1200.0),
new Product("Mouse", null),
new Product("Keyboard", 50.0)
);
// 按價格排序,null 價格排在最后
List<Product> sortedByPrice = products.stream()
.sorted(Comparator.comparing(Product::getPrice, Comparator.nullsLast(Double::compare)))
.toList();五、逆序排序與自定義比較邏輯
1. 逆序排序
使用 Comparator.reverseOrder() 或 Comparator.comparing().reversed() 實現(xiàn)逆序:
// 字符串長度逆序排序
List<String> reversedByLength = words.stream()
.sorted(Comparator.comparing(String::length).reversed())
.toList();
// 另一種逆序?qū)懛?
List<String> reversedByLength2 = words.stream()
.sorted((s1, s2) -> s2.length() - s1.length())
.toList();2. 自定義復(fù)雜比較邏輯
對于更復(fù)雜的業(yè)務(wù)規(guī)則,可以實現(xiàn) Comparator 接口:
// 示例:按字符串長度排序,長度相同則按字母順序排序
List<String> complexSort = words.stream()
.sorted(new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
int lengthCompare = Integer.compare(s1.length(), s2.length());
if (lengthCompare != 0) {
return lengthCompare;
}
return s1.compareTo(s2);
}
})
.toList();
// 使用 Lambda 簡化
List<String> complexSort2 = words.stream()
.sorted((s1, s2) -> {
int lengthCompare = Integer.compare(s1.length(), s2.length());
return lengthCompare != 0 ? lengthCompare : s1.compareTo(s2);
})
.toList();六、性能優(yōu)化與注意事項
1. 基本類型與裝箱類型
對于基本類型(如 int、long、double),優(yōu)先使用 comparingInt、comparingLong、comparingDouble 避免裝箱拆箱開銷:
// 性能優(yōu)化示例
List<Integer> numbers = Arrays.asList(5, 3, 8, 1, 2);
// 避免裝箱
List<Integer> optimizedSort = numbers.stream()
.sorted(Comparator.comparingInt(Integer::intValue))
.toList();2. 排序穩(wěn)定性
Stream.sorted() 使用的是穩(wěn)定排序算法(TimSort),即相等元素的相對順序不會改變。這在多級排序中尤為重要:
// 示例:先按部門排序,再按工資排序
List<Employee> employees = ...;
List<Employee> sortedEmployees = employees.stream()
.sorted(Comparator.comparing(Employee::getDepartment)
.thenComparingDouble(Employee::getSalary))
.toList();3. 并行流排序性能
在并行流中,排序操作可能會導(dǎo)致性能下降,因為需要全局?jǐn)?shù)據(jù)重組。謹(jǐn)慎在并行流中使用復(fù)雜排序:
// 并行流排序示例
List<Integer> parallelSorted = numbers.parallelStream()
.sorted()
.toList();
七、實戰(zhàn)案例
1. 電商商品排序系統(tǒng)
class Product {
private String name;
private double price;
private int salesVolume;
private LocalDateTime createTime;
// 構(gòu)造方法、getter和setter略
}
// 按價格升序排序
List<Product> sortedByPrice = products.stream()
.sorted(Comparator.comparingDouble(Product::getPrice))
.toList();
// 按銷量降序,銷量相同則按創(chuàng)建時間降序
List<Product> sortedBySalesAndTime = products.stream()
.sorted(Comparator.comparingInt(Product::getSalesVolume).reversed()
.thenComparing(Product::getCreateTime, Comparator.reverseOrder()))
.toList();2. 日志時間戳排序
class LogEntry {
private LocalDateTime timestamp;
private String message;
private LogLevel level;
// 構(gòu)造方法、getter和setter略
}
// 按時間戳排序
List<LogEntry> sortedLogs = logs.stream()
.sorted(Comparator.comparing(LogEntry::getTimestamp))
.toList();
// 按日志級別排序(自定義順序:ERROR > WARN > INFO > DEBUG)
List<LogEntry> sortedByLevel = logs.stream()
.sorted(Comparator.comparing(LogEntry::getLevel,
Comparator.comparingInt(level -> {
switch (level) {
case ERROR: return 4;
case WARN: return 3;
case INFO: return 2;
case DEBUG: return 1;
default: return 0;
}
}).reversed()))
.toList();八、總結(jié)與最佳實踐
優(yōu)先使用方法引用和靜態(tài)工具方法:
// 推薦寫法 sorted(Comparator.comparing(Person::getAge)) // 避免冗余的 Lambda sorted((p1, p2) -> p1.getAge() - p2.getAge())
多級排序使用鏈?zhǔn)秸{(diào)用:
sorted(Comparator.comparing(Person::getDepartment)
.thenComparing(Person::getAge)
.thenComparing(Person::getName))
處理 null 值:
sorted(Comparator.nullsLast(Comparator.comparing(Person::getName)))
基本類型優(yōu)化:
sorted(Comparator.comparingInt(Person::getAge)) // 避免裝箱
復(fù)雜比較器提取為常量:
public static final Comparator<Person> AGE_NAME_COMPARATOR =
Comparator.comparingInt(Person::getAge)
.thenComparing(Person::getName);
// 使用時
sorted(AGE_NAME_COMPARATOR)通過掌握 sorted() 方法的各種用法,你可以靈活應(yīng)對各種復(fù)雜的排序需求,編寫出簡潔、高效且易于維護(hù)的代碼。在實際開發(fā)中,合理運用 Comparator 的各種工具方法和特性,能夠顯著提升代碼質(zhì)量和開發(fā)效率。
到此這篇關(guān)于Java Stream 的 sorted 方法實現(xiàn)自定義排序全解析的文章就介紹到這了,更多相關(guān)java stream sorted方法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java編程實現(xiàn)求質(zhì)數(shù)與因式分解代碼分享
這篇文章主要介紹了Java編程實現(xiàn)求質(zhì)數(shù)與因式分解代碼分享,對二者的概念作了簡單介紹(多此一舉,哈哈),都是小學(xué)數(shù)學(xué)老師的任務(wù),然后分享了求解質(zhì)數(shù)和因式分解的Java代碼,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12
Spring Task定時任務(wù)的實現(xiàn)詳解
這篇文章主要介紹了SpringBoot定時任務(wù)功能詳細(xì)解析,這次的功能開發(fā)過程中也算是對其內(nèi)涵的進(jìn)一步了解,以后遇到定時任務(wù)的處理也更清晰,更有效率了,對SpringBoot定時任務(wù)相關(guān)知識感興趣的朋友一起看看吧2022-08-08
Java經(jīng)典快排思想以及快排的改進(jìn)講解
今天小編就為大家分享一篇關(guān)于Java經(jīng)典快排思想以及快排的改進(jìn)講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01
使用springmvc運行流程分析,手寫spring框架嘗試
這篇文章主要介紹了使用springmvc運行流程分析,手寫spring框架嘗試,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
使用vue3.x+vite+element-ui+vue-router+vuex+axios搭建項目
因為vue3出了一段時間了,element也出了基于vue3.x版本的element-plus,這篇文章就拿他們搭建一個項目,希望能給你帶來幫助2021-08-08

