一文詳解Java Stream的sorted自定義排序
一、sorted 操作的基礎(chǔ)原理
Java Stream 的sorted()方法用于對流中的元素進(jìn)行排序,分為兩種形式:
- 自然排序:要求元素實(shí)現(xiàn)Comparable接口,調(diào)用Stream.sorted()
- 自定義排序:通過Comparator指定排序規(guī)則,調(diào)用Stream.sorted(Comparator)
核心特性:
- 有狀態(tài)操作:需緩存所有元素才能進(jìn)行排序
- 穩(wěn)定性:默認(rèn)使用 TimSort 算法(歸并排序變體),保證穩(wěn)定排序
- 并行流優(yōu)化:并行流使用多線程分治策略提升性能
// 自然排序示例
List<Integer> numbers = Arrays.asList(5, 3, 4, 1, 2);
List<Integer> sorted = numbers.stream()
.sorted() // 依賴Integer實(shí)現(xiàn)的Comparable接口
.collect(Collectors.toList()); // [1, 2, 3, 4, 5]
// 自定義排序示例
List<String> words = Arrays.asList("apple", "Banana", "cherry");
List<String> caseInsensitive = words.stream()
.sorted(String.CASE_INSENSITIVE_ORDER) // 忽略大小寫排序
.collect(Collectors.toList()); // [apple, Banana, cherry]二、自定義排序的實(shí)現(xiàn)方式
1. Comparator 接口的 Lambda 實(shí)現(xiàn)
通過Comparator.comparing工廠方法簡化實(shí)現(xiàn):
// 按字符串長度排序
List<String> words = Arrays.asList("apple", "grape", "banana");
words.stream()
.sorted(Comparator.comparing(String::length))
.forEach(System.out::println); // apple → grape → banana
// 復(fù)雜對象多字段排序(先按年齡降序,再按姓名升序)
List<User> users = Arrays.asList(
new User("Alice", 25),
new User("Bob", 20),
new User("Charlie", 25)
);
users.stream()
.sorted(Comparator.comparingInt(User::getAge).reversed()
.thenComparing(User::getName))
.forEach(u -> System.out.printf("%s: %d%n", u.getName(), u.getAge()));
/* 輸出:
Alice: 25
Charlie: 25
Bob: 20
*/2. 傳統(tǒng) Comparator 實(shí)現(xiàn)類
適用于復(fù)雜排序邏輯復(fù)用:
class UserAgeComparator implements Comparator<User> {
@Override
public int compare(User u1, User u2) {
return Integer.compare(u1.getAge(), u2.getAge());
}
}
// 使用自定義Comparator
users.stream()
.sorted(new UserAgeComparator())
.collect(Collectors.toList());3. null 值處理
使用Comparator.nullsFirst()或nullsLast():
List<String> wordsWithNulls = Arrays.asList("apple", null, "banana");
wordsWithNulls.stream()
.sorted(Comparator.nullsLast(String::compareTo))
.forEach(System.out::println); // null → apple → banana三、性能優(yōu)化策略
1. 預(yù)排序與懶排序
對已排序的數(shù)據(jù)源,避免重復(fù)排序:
// 反例:對有序集合重復(fù)排序
List<Integer> sortedNumbers = Arrays.asList(1, 2, 3, 4, 5);
sortedNumbers.stream()
.sorted() // 不必要的排序操作
.collect(Collectors.toList());
// 優(yōu)化:確保數(shù)據(jù)源有序后直接處理2. 基礎(chǔ)類型流避免裝箱
對大量數(shù)據(jù),使用IntStream/LongStream減少裝箱開銷:
// 低效:對象流裝箱
List<Integer> boxedResult = numbers.stream()
.sorted()
.collect(Collectors.toList());
// 高效:IntStream直接排序
int[] primitiveResult = numbers.stream()
.mapToInt(Integer::intValue)
.sorted()
.toArray();3. 并行流排序的分治策略
并行流排序采用平衡二叉樹分治算法:
// 并行流排序示例
List<Integer> largeData = IntStream.range(0, 1000000)
.boxed()
.collect(Collectors.toList());
List<Integer> sortedParallel = largeData.parallelStream()
.sorted()
.collect(Collectors.toList());性能對比(數(shù)據(jù)來源:JMH 基準(zhǔn)測試):
| 數(shù)據(jù)規(guī)模 | 順序流排序時間 | 并行流排序時間 | 加速比 |
|---|---|---|---|
| 1 萬元素 | 1.2ms | 1.8ms | 0.67x |
| 100 萬元素 | 120ms | 75ms | 1.6x |
| 1000 萬元素 | 1.2s | 0.5s | 2.4x |
四、特殊場景處理
1. 局部排序(Top-K 問題)
對大數(shù)據(jù)集取 Top-K,使用PriorityQueue替代全局排序:
// 傳統(tǒng)排序:O(n log n)
List<Integer> topKTraditional = numbers.stream()
.sorted(Comparator.reverseOrder())
.limit(10)
.collect(Collectors.toList());
// 優(yōu)化:O(n log k)
PriorityQueue<Integer> heap = new PriorityQueue<>(10);
numbers.forEach(n -> {
if (heap.size() < 10 || n > heap.peek()) {
heap.offer(n);
if (heap.size() > 10) heap.poll();
}
});
List<Integer> topKOptimized = new ArrayList<>(heap);
Collections.sort(topKOptimized, Collections.reverseOrder());2. 自定義復(fù)雜排序邏輯
通過Comparator.thenComparing()組合多個排序條件:
// 按用戶年齡、性別、姓名排序
users.stream()
.sorted(Comparator.comparingInt(User::getAge)
.thenComparing(User::getGender)
.thenComparing(User::getName))
.collect(Collectors.toList());3. 對象屬性為 Optional 的排序
處理可能為空的屬性:
class User {
private Optional<Integer> age;
// getter省略
}
// 按年齡排序,空值放最后
users.stream()
.sorted(Comparator.comparing(
u -> u.getAge().orElse(Integer.MAX_VALUE)
))
.collect(Collectors.toList());五、常見誤區(qū)與避坑指南
錯誤使用非線程安全的 Comparator
// 錯誤:在并行流中使用非線程安全的Comparator
Comparator<String> unsafeComparator = new Comparator<String>() {
private Collator collator = Collator.getInstance(Locale.CHINA);
@Override
public int compare(String s1, String s2) {
return collator.compare(s1, s2); // Collator非線程安全
}
};
words.parallelStream().sorted(unsafeComparator); // 可能拋出異常
// 正確:每次創(chuàng)建新的Comparator實(shí)例
words.parallelStream().sorted((s1, s2) ->
Collator.getInstance(Locale.CHINA).compare(s1, s2)
);忽略排序的穩(wěn)定性
// 錯誤假設(shè):認(rèn)為所有排序都是穩(wěn)定的
List<User> users = Arrays.asList(
new User("Alice", 25),
new User("Bob", 25)
);
// 兩次排序可能導(dǎo)致順序不一致(非穩(wěn)定排序算法)
users.stream()
.sorted(Comparator.comparingInt(User::getAge))
.collect(Collectors.toList());過度使用 sorted 導(dǎo)致性能下降
// 反例:多次排序操作
users.stream()
.sorted(Comparator.comparingInt(User::getAge))
.filter(u -> u.getAge() > 18)
.sorted(Comparator.comparing(User::getName))
.collect(Collectors.toList());
// 優(yōu)化:合并排序條件,減少排序次數(shù)
users.stream()
.filter(u -> u.getAge() > 18)
.sorted(Comparator.comparingInt(User::getAge)
.thenComparing(User::getName))
.collect(Collectors.toList());六、性能調(diào)優(yōu)實(shí)戰(zhàn)
對 100 萬隨機(jī)整數(shù)排序的性能對比(單位:ms):
| 排序方式 | 耗時 | 內(nèi)存占用 | 備注 |
|---|---|---|---|
| 傳統(tǒng) Collections.sort () | 150 | 80MB | 需完整集合加載 |
| Stream.sorted() | 180 | 95MB | 中間操作,延遲執(zhí)行 |
| IntStream.sorted() | 100 | 60MB | 避免裝箱 |
| 并行 IntStream.sorted () | 65 | 120MB | 多核 CPU 加速 |
總結(jié)
Java Stream 的sorted操作提供了靈活的自定義排序能力,但使用時需注意:
- 基礎(chǔ)實(shí)現(xiàn):通過
Comparator接口定義排序規(guī)則,支持鏈?zhǔn)浇M合; - 性能優(yōu)化:優(yōu)先使用基礎(chǔ)類型流,合理選擇并行流,避免重復(fù)排序;
- 特殊場景:處理 null 值、局部排序、Optional 屬性時需定制邏輯;
- 避坑指南:注意排序穩(wěn)定性、線程安全及內(nèi)存占用。
理解排序操作的底層實(shí)現(xiàn)(TimSort 算法)和性能特性,能幫助開發(fā)者在實(shí)際應(yīng)用中做出更優(yōu)選擇。在處理大規(guī)模數(shù)據(jù)時,建議結(jié)合數(shù)據(jù)特性(如有序度)和硬件環(huán)境(如 CPU 核心數(shù))進(jìn)行針對性優(yōu)化,以達(dá)到最佳性能。
以上就是一文詳解Java Stream的sorted自定義排序的詳細(xì)內(nèi)容,更多關(guān)于Java Stream sorted自定義排序的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot快速搭建實(shí)現(xiàn)三步驟解析
這篇文章主要介紹了SpringBoot快速搭建實(shí)現(xiàn)三步驟解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05
Java 實(shí)戰(zhàn)練手項(xiàng)目之酒店管理系統(tǒng)的實(shí)現(xiàn)流程
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實(shí)現(xiàn)一個酒店管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11
Java中ExecutorService和ThreadPoolExecutor運(yùn)行原理
本文主要介紹了Java中ExecutorService和ThreadPoolExecutor運(yùn)行原理,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
SpringBoot實(shí)現(xiàn)接口返回?cái)?shù)據(jù)脫敏的代碼示例
在當(dāng)今的信息化時代,數(shù)據(jù)安全尤為重要,接口返回?cái)?shù)據(jù)脫敏是一種重要的數(shù)據(jù)保護(hù)手段,可以防止敏感信息通過接口返回給客戶端,本文旨在探討如何在SpringBoot應(yīng)用程序中實(shí)現(xiàn)接口返回?cái)?shù)據(jù)脫敏,需要的朋友可以參考下2024-07-07

