Java Stream 的 forEachOrdered 與 forEach的區(qū)別與適用場(chǎng)景
在 Java Stream API 中,forEach 和 forEachOrdered 是兩個(gè)常用的終止操作,用于對(duì)流中的元素執(zhí)行迭代處理。雖然它們的功能看似相似,但在執(zhí)行順序、并行處理和性能特性等方面存在重要差異。本文將從多個(gè)維度深入分析這兩個(gè)方法的區(qū)別與適用場(chǎng)景。
一、核心定義與基本用法
1. forEach 方法
void forEach(Consumer<? super T> action);
- 特性:
- 不保證元素的處理順序(特別是在并行流中)
- 對(duì)并行流,可能在多個(gè)線程中同時(shí)執(zhí)行 action
- 是一個(gè)短路操作(Short-circuiting),可能提前終止
2. forEachOrdered 方法
void forEachOrdered(Consumer<? super T> action);
- 特性:
- 保證元素按照流的源順序處理(即使在并行流中)
- 在并行流中,可能會(huì)導(dǎo)致線程同步開(kāi)銷(xiāo)
- 不具備短路特性,必須處理所有元素
二、執(zhí)行順序?qū)Ρ?/h2>
1. 順序流中的行為
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// forEach(順序流)
numbers.stream()
.forEach(n -> System.out.print(n + " ")); // 輸出:1 2 3 4 5(順序一致)
// forEachOrdered(順序流)
numbers.stream()
.forEachOrdered(n -> System.out.print(n + " ")); // 輸出:1 2 3 4 5(順序一致)在順序流中,兩者的執(zhí)行順序相同,均保持源數(shù)據(jù)的順序。
2. 并行流中的行為
// forEach(并行流)
numbers.parallelStream()
.forEach(n -> System.out.print(n + " ")); // 輸出:可能為 3 1 4 2 5(順序不確定)
// forEachOrdered(并行流)
numbers.parallelStream()
.forEachOrdered(n -> System.out.print(n + " ")); // 輸出:1 2 3 4 5(強(qiáng)制保持順序)在并行流中,forEach 不保證順序,而 forEachOrdered 通過(guò)同步機(jī)制強(qiáng)制保持順序。
三、并行處理性能對(duì)比
由于 forEachOrdered 需要維護(hù)處理順序,在并行流中可能引入顯著的性能開(kāi)銷(xiāo):
| 場(chǎng)景 | forEach 性能 | forEachOrdered 性能 |
|---|---|---|
| 順序流 | 無(wú)額外開(kāi)銷(xiāo) | 無(wú)額外開(kāi)銷(xiāo) |
| 并行流(無(wú)需順序) | 高效(充分并行) | 低(線程同步開(kāi)銷(xiāo)大) |
| 并行流(必須順序) | 不適用(順序不確定) | 可接受(但低于順序流) |
四、適用場(chǎng)景分析
1. forEach 的典型場(chǎng)景
無(wú)需順序保證的并行處理:
// 并行計(jì)算元素的平方和(順序不影響結(jié)果)
AtomicInteger sum = new AtomicInteger();
numbers.parallelStream()
.forEach(n -> sum.addAndGet(n * n));
IO 密集型操作:
// 并行下載多個(gè)文件(順序無(wú)關(guān))
urls.parallelStream()
.forEach(url -> downloadFile(url));
2. forEachOrdered 的典型場(chǎng)景
需要嚴(yán)格順序的并行處理:
// 并行打印帶序號(hào)的元素(順序必須與源一致)
List<String> messages = Arrays.asList("A", "B", "C", "D");
AtomicInteger counter = new AtomicInteger(1);
messages.parallelStream()
.forEachOrdered(msg ->
System.out.println("[" + counter.getAndIncrement() + "] " + msg));
// 輸出:
// [1] A
// [2] B
// [3] C
// [4] D狀態(tài)依賴(lài)的處理邏輯:
// 按順序處理訂單(后續(xù)訂單依賴(lài)前面的處理結(jié)果)
orders.parallelStream()
.forEachOrdered(order -> processOrder(order));
五、注意事項(xiàng)與最佳實(shí)踐
- 避免在并行流中使用 forEachOrdered:
- 除非必須保持順序,否則應(yīng)優(yōu)先使用
forEach以獲得更好的并行性能
- 除非必須保持順序,否則應(yīng)優(yōu)先使用
- 線程安全問(wèn)題:
- 當(dāng)在并行流中使用
forEach或forEachOrdered時(shí),確保Consumer是線程安全的
- 當(dāng)在并行流中使用
- 性能測(cè)試:
- 對(duì)于關(guān)鍵業(yè)務(wù)邏輯,建議對(duì)比
forEach和forEachOrdered的性能差異 - 示例測(cè)試代碼:
- 對(duì)于關(guān)鍵業(yè)務(wù)邏輯,建議對(duì)比
long startTime = System.nanoTime();
numbers.parallelStream().forEach(n -> process(n));
long duration = System.nanoTime() - startTime;
System.out.println("forEach 耗時(shí):" + duration / 1_000_000 + "ms");
startTime = System.nanoTime();
numbers.parallelStream().forEachOrdered(n -> process(n));
duration = System.nanoTime() - startTime;
System.out.println("forEachOrdered 耗時(shí):" + duration / 1_000_000 + "ms");替代方案:
// 并行處理后保持順序
List<Integer> processed = numbers.parallelStream()
.map(n -> process(n))
.collect(Collectors.toList());
processed.forEach(System.out::println); // 按順序輸出
若需要保持順序且追求更好的并行性能,可考慮使用 collect 或 toList 后再處理
六、總結(jié)
| 特性 | forEach | forEachOrdered |
|---|---|---|
| 順序保證 | 不保證(并行流中亂序) | 保證(即使在并行流中) |
| 并行性能 | 高(無(wú)同步開(kāi)銷(xiāo)) | 低(需線程同步) |
| 短路特性 | 支持(可能提前終止) | 不支持(必須處理所有元素) |
| 適用場(chǎng)景 | 無(wú)需順序的并行操作 | 需要順序的并行操作或順序流 |
在實(shí)際開(kāi)發(fā)中,應(yīng)根據(jù)業(yè)務(wù)需求合理選擇:若處理順序不影響結(jié)果,優(yōu)先使用 forEach;若必須保持順序,可在順序流中使用 forEach 或在并行流中使用 forEachOrdered,但需注意性能開(kāi)銷(xiāo)。通過(guò)理解這兩個(gè)方法的本質(zhì)差異,可以編寫(xiě)出更高效、更健壯的代碼。
到此這篇關(guān)于Java Stream 的 forEachOrdered 與 forEach的區(qū)別與適用場(chǎng)景的文章就介紹到這了,更多相關(guān)Java Stream forEach內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java利用序列化實(shí)現(xiàn)對(duì)象深度clone的方法
這篇文章主要介紹了Java利用序列化實(shí)現(xiàn)對(duì)象深度clone的方法,實(shí)例分析了java序列化及對(duì)象克隆的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
Java 集合框架之List 的使用(附小游戲練習(xí))
這篇文章主要介紹Java 集合框架中List 的使用,下面文章將圍繞Java 集合框架中List 的使用展開(kāi)話題,并附上一些小游戲練習(xí),需要的朋友可以參考一下2021-10-10
Spring-boot JMS 發(fā)送消息慢的解決方法
這篇文章主要為大家詳細(xì)介紹了Spring-boot JMS 發(fā)送消息慢的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
基于EasyExcel實(shí)現(xiàn)百萬(wàn)級(jí)數(shù)據(jù)導(dǎo)入導(dǎo)出詳解
大數(shù)據(jù)的導(dǎo)入和導(dǎo)出,相信大家在日常的開(kāi)發(fā)、面試中都會(huì)遇到。本文將為大家詳細(xì)介紹一下如何利用EasyExcel實(shí)現(xiàn)百萬(wàn)級(jí)數(shù)據(jù)導(dǎo)入導(dǎo)出,需要的可以參考一下2023-01-01
SpringBoot @NotBlank錯(cuò)誤的解決方案
這篇文章主要介紹了SpringBoot @NotBlank錯(cuò)誤的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Mybatis-Plus根據(jù)自定義注解實(shí)現(xiàn)自動(dòng)加解密的示例代碼
我們把數(shù)據(jù)存到數(shù)據(jù)庫(kù)的時(shí)候,有些敏感字段是需要加密的,從數(shù)據(jù)庫(kù)查出來(lái)再進(jìn)行解密,如果我們使用的是Mybatis框架,那就跟著一起探索下如何使用框架的攔截器功能實(shí)現(xiàn)自動(dòng)加解密吧,需要的朋友可以參考下2024-06-06

