Java Stream 的 forEachOrdered 與 forEach的區(qū)別與適用場景
在 Java Stream API 中,forEach 和 forEachOrdered 是兩個常用的終止操作,用于對流中的元素執(zhí)行迭代處理。雖然它們的功能看似相似,但在執(zhí)行順序、并行處理和性能特性等方面存在重要差異。本文將從多個維度深入分析這兩個方法的區(qū)別與適用場景。
一、核心定義與基本用法
1. forEach 方法
void forEach(Consumer<? super T> action);
- 特性:
- 不保證元素的處理順序(特別是在并行流中)
- 對并行流,可能在多個線程中同時執(zhí)行 action
- 是一個短路操作(Short-circuiting),可能提前終止
2. forEachOrdered 方法
void forEachOrdered(Consumer<? super T> action);
- 特性:
- 保證元素按照流的源順序處理(即使在并行流中)
- 在并行流中,可能會導(dǎ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 通過同步機(jī)制強(qiáng)制保持順序。
三、并行處理性能對比
由于 forEachOrdered 需要維護(hù)處理順序,在并行流中可能引入顯著的性能開銷:
| 場景 | forEach 性能 | forEachOrdered 性能 |
|---|---|---|
| 順序流 | 無額外開銷 | 無額外開銷 |
| 并行流(無需順序) | 高效(充分并行) | 低(線程同步開銷大) |
| 并行流(必須順序) | 不適用(順序不確定) | 可接受(但低于順序流) |
四、適用場景分析
1. forEach 的典型場景
無需順序保證的并行處理:
// 并行計(jì)算元素的平方和(順序不影響結(jié)果)
AtomicInteger sum = new AtomicInteger();
numbers.parallelStream()
.forEach(n -> sum.addAndGet(n * n));
IO 密集型操作:
// 并行下載多個文件(順序無關(guān))
urls.parallelStream()
.forEach(url -> downloadFile(url));
2. forEachOrdered 的典型場景
需要嚴(yán)格順序的并行處理:
// 并行打印帶序號的元素(順序必須與源一致)
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)依賴的處理邏輯:
// 按順序處理訂單(后續(xù)訂單依賴前面的處理結(jié)果)
orders.parallelStream()
.forEachOrdered(order -> processOrder(order));
五、注意事項(xiàng)與最佳實(shí)踐
- 避免在并行流中使用 forEachOrdered:
- 除非必須保持順序,否則應(yīng)優(yōu)先使用
forEach以獲得更好的并行性能
- 除非必須保持順序,否則應(yīng)優(yōu)先使用
- 線程安全問題:
- 當(dāng)在并行流中使用
forEach或forEachOrdered時,確保Consumer是線程安全的
- 當(dāng)在并行流中使用
- 性能測試:
- 對于關(guān)鍵業(yè)務(wù)邏輯,建議對比
forEach和forEachOrdered的性能差異 - 示例測試代碼:
- 對于關(guān)鍵業(yè)務(wù)邏輯,建議對比
long startTime = System.nanoTime();
numbers.parallelStream().forEach(n -> process(n));
long duration = System.nanoTime() - startTime;
System.out.println("forEach 耗時:" + duration / 1_000_000 + "ms");
startTime = System.nanoTime();
numbers.parallelStream().forEachOrdered(n -> process(n));
duration = System.nanoTime() - startTime;
System.out.println("forEachOrdered 耗時:" + 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 |
|---|---|---|
| 順序保證 | 不保證(并行流中亂序) | 保證(即使在并行流中) |
| 并行性能 | 高(無同步開銷) | 低(需線程同步) |
| 短路特性 | 支持(可能提前終止) | 不支持(必須處理所有元素) |
| 適用場景 | 無需順序的并行操作 | 需要順序的并行操作或順序流 |
在實(shí)際開發(fā)中,應(yīng)根據(jù)業(yè)務(wù)需求合理選擇:若處理順序不影響結(jié)果,優(yōu)先使用 forEach;若必須保持順序,可在順序流中使用 forEach 或在并行流中使用 forEachOrdered,但需注意性能開銷。通過理解這兩個方法的本質(zhì)差異,可以編寫出更高效、更健壯的代碼。
到此這篇關(guān)于Java Stream 的 forEachOrdered 與 forEach的區(qū)別與適用場景的文章就介紹到這了,更多相關(guān)Java Stream forEach內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java數(shù)據(jù)輸出打印流PrintStream和PrintWriter面試精講
這篇文章主要為大家介紹了java數(shù)據(jù)輸出打印流PrintStream和PrintWriter面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10
Java8深入學(xué)習(xí)系列(三)你可能忽略了的新特性
一提到Java 8就只能聽到lambda,但這不過是其中的一個而已,Java 8還有許多新的特性,有一些功能強(qiáng)大的新類或者新的用法,還有一些功能則是早就應(yīng)該加到Java里了,所以下面這篇文章主要給大家介紹了關(guān)于Java8中大家可能忽略了的一些新特性,需要的朋友可以參考下。2017-08-08
為什么wait和notify必須放在synchronized中使用
這篇文章主要介紹了為什么wait和notify必須放在synchronized中使用,文章圍繞主題的相關(guān)問題展開詳細(xì)介紹,具有一定的參考價值,需要的小伙伴可以參考以參考一下2022-05-05
Java報(bào)錯:java.util.concurrent.ExecutionException的解決辦法
在Java并發(fā)編程中,我們經(jīng)常使用java.util.concurrent包提供的工具來管理和協(xié)調(diào)多個線程的執(zhí)行,va并發(fā)編程中,然而,在使用這些工具時,可能會遇到各種各樣的異常,其中之一就是java.util.concurrent.ExecutionException,本文將詳細(xì)分析這種異常的背景、可能的原因2024-09-09
實(shí)例講解Java的MyBatis框架對MySQL中數(shù)據(jù)的關(guān)聯(lián)查詢
這里我們來以實(shí)例講解Java的MyBatis框架對MySQL中數(shù)據(jù)的關(guān)聯(lián)查詢,包括一對多、多對一的關(guān)聯(lián)查詢以及自身關(guān)聯(lián)映射的方法等,需要的朋友可以參考下2016-06-06
Mybatis插入時返回自增主鍵方式(selectKey和useGeneratedKeys)
這篇文章主要介紹了Mybatis插入時返回自增主鍵方式(selectKey和useGeneratedKeys),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringBoot最簡單的定時任務(wù)@Scheduler的使用及解讀
這篇文章主要介紹了SpringBoot最簡單的定時任務(wù)@Scheduler的使用及解讀,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-03-03
JavaWeb response和request對象原理及實(shí)例解析
這篇文章主要介紹了JavaWeb response和request對象原理及實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-11-11
Java的Synchronized關(guān)鍵字學(xué)習(xí)指南(全面 & 詳細(xì))
這篇文章主要給大家介紹了關(guān)于Java的Synchronized關(guān)鍵字的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03

