stream.findFirst().get()?報(bào)錯(cuò)?NoSuchElementException的解決方案
stream.findFirst().get() 報(bào)錯(cuò) NoSuchElementException,核心原因是:Stream 流中沒(méi)有匹配到任何元素,findFirst() 返回空的 Optional 對(duì)象,此時(shí)調(diào)用 get() 會(huì)直接拋出異常(Optional.get() 約定:空 Optional 調(diào)用 get () 必拋異常)。
一、報(bào)錯(cuò)根源拆解
1. 關(guān)鍵邏輯:Optional 的設(shè)計(jì)初衷
findFirst() 返回 Optional<T> 類型,目的是「明確告知調(diào)用者:結(jié)果可能為空」,強(qiáng)制開(kāi)發(fā)者處理 “無(wú)結(jié)果” 的場(chǎng)景。而直接調(diào)用 get() 相當(dāng)于 “無(wú)視空值風(fēng)險(xiǎn)”,一旦流為空就會(huì)報(bào)錯(cuò):
// 錯(cuò)誤示例:流為空時(shí),findFirst() 返回 Optional.empty(),get() 拋異常
List<String> list = Collections.emptyList();
String result = list.stream()
.filter(s -> s.startsWith("A")) // 無(wú)匹配元素
.findFirst()
.get(); // 報(bào)錯(cuò):NoSuchElementException: No value present2. 常見(jiàn)觸發(fā)場(chǎng)景
- 流的數(shù)據(jù)源為空(如
emptyList()、數(shù)據(jù)庫(kù)查詢無(wú)結(jié)果); filter()過(guò)濾條件過(guò)于嚴(yán)格,沒(méi)有元素滿足;- 流經(jīng)過(guò)
map()/flatMap()后變?yōu)榭樟鳌?/li>
二、解決方案(按安全優(yōu)先級(jí)排序)
核心原則:永遠(yuǎn)不要直接調(diào)用 Optional.get(),除非能 100% 確定流中一定有元素。以下是 5 種安全處理方式,按需選擇:
方案 1:用 orElse() 給默認(rèn)值(最常用)
流為空時(shí),返回預(yù)設(shè)的默認(rèn)值,將.get()替換為.orElse(null)(適合 “無(wú)結(jié)果時(shí)用默認(rèn)值兜底” 的場(chǎng)景):
List<String> list = Collections.emptyList();
// 無(wú)匹配元素時(shí),返回默認(rèn)值 "默認(rèn)值"
String result = list.stream()
.filter(s -> s.startsWith("A"))
.findFirst()
.orElse("默認(rèn)值"); // 安全:不會(huì)拋異常
System.out.println(result); // 輸出:默認(rèn)值方案 2:用 orElseGet() 延遲生成默認(rèn)值(性能更優(yōu))
與 orElse() 類似,但默認(rèn)值是 “懶加載” 的(只有流為空時(shí)才執(zhí)行 Supplier 邏輯,適合默認(rèn)值生成耗時(shí)的場(chǎng)景,如數(shù)據(jù)庫(kù)查詢、復(fù)雜計(jì)算):
// 無(wú)結(jié)果時(shí),調(diào)用 Supplier 生成默認(rèn)值(延遲執(zhí)行)
String result = list.stream()
.filter(s -> s.startsWith("A"))
.findFirst()
.orElseGet(() -> generateDefaultValue()); // 流為空時(shí)才執(zhí)行 generateDefaultValue()
// 生成默認(rèn)值的方法(示例)
private String generateDefaultValue() {
System.out.println("生成默認(rèn)值(僅流為空時(shí)執(zhí)行)");
return "延遲生成的默認(rèn)值";
}方案 3:用 orElseThrow() 拋?zhàn)远x異常(明確無(wú)結(jié)果是錯(cuò)誤場(chǎng)景)
如果 “流必須有結(jié)果”(如根據(jù) ID 查詢核心配置,無(wú)結(jié)果則系統(tǒng)無(wú)法運(yùn)行),拋出自定義異常,比默認(rèn)的 NoSuchElementException 更易排查:
try {
String result = list.stream()
.filter(s -> s.startsWith("A"))
.findFirst()
.orElseThrow(() -> new RuntimeException("未找到匹配的元素")); // 自定義異常
} catch (RuntimeException e) {
// 處理異常(日志記錄、返回錯(cuò)誤響應(yīng)等)
e.printStackTrace();
}- 推薦場(chǎng)景:業(yè)務(wù)上 “無(wú)結(jié)果” 屬于異常情況(如用戶查詢不存在的訂單)。
方案 4:用 ifPresent() 處理非空結(jié)果(無(wú)返回值)
如果只需要 “有結(jié)果時(shí)執(zhí)行某個(gè)邏輯,無(wú)結(jié)果則不做任何事”(如異步通知、日志打?。?,用 ifPresent() 避免空判斷:
list.stream()
.filter(s -> s.startsWith("A"))
.findFirst()
.ifPresent(result -> {
// 只有流不為空時(shí),才執(zhí)行此邏輯(無(wú)需手動(dòng)判空)
System.out.println("找到匹配元素:" + result);
doSomething(result); // 業(yè)務(wù)邏輯
});方案 5:用 isPresent() 手動(dòng)判空(最靈活)
先判斷 Optional 是否有值,再?zèng)Q定后續(xù)邏輯(適合需要 “無(wú)結(jié)果時(shí)執(zhí)行復(fù)雜邏輯” 的場(chǎng)景,如返回錯(cuò)誤碼 + 日志記錄):
Optional<String> optional = list.stream()
.filter(s -> s.startsWith("A"))
.findFirst();
if (optional.isPresent()) {
String result = optional.get(); // 此時(shí) get() 安全(已判空)
doSomething(result);
} else {
// 無(wú)結(jié)果時(shí)的處理邏輯
System.out.println("未找到匹配元素");
return "錯(cuò)誤碼";
}三、進(jìn)階優(yōu)化:避免流為空的前置校驗(yàn)
如果流的數(shù)據(jù)源是集合 / 數(shù)組,可先做前置校驗(yàn),減少流處理的空值風(fēng)險(xiǎn):
List<String> list = Collections.emptyList();
// 前置校驗(yàn):先判斷集合是否為空,再處理流
if (CollectionUtils.isEmpty(list)) { // 用 Apache Commons 工具類,或手動(dòng)判空
System.out.println("集合為空,直接返回默認(rèn)值");
return "默認(rèn)值";
}
// 集合非空時(shí),再處理流(減少 Optional 空值概率)
String result = list.stream()
.filter(s -> s.startsWith("A"))
.findFirst()
.orElse("無(wú)匹配元素");四、常見(jiàn)誤區(qū)提醒
不要用 get() 配合 filter() 判空(冗余且不安全):
// 錯(cuò)誤示例:冗余且仍有風(fēng)險(xiǎn)(若 filter 后為空,get() 仍報(bào)錯(cuò))
Optional<String> optional = list.stream().filter(...).findFirst();
if (optional.filter(Objects::nonNull).isPresent()) { // 多余的 filter
String result = optional.get();
}正確做法:直接用 isPresent() 或 ifPresent(),無(wú)需額外 filter(Objects::nonNull)(findFirst() 已處理流中元素的空值)。
不要忽略 Optional 的其他有用方法:
map():Optional 非空時(shí),對(duì)結(jié)果做轉(zhuǎn)換(避免嵌套 Optional);
// 示例:找到元素后,轉(zhuǎn)為長(zhǎng)度(無(wú)元素時(shí)返回 0)
int length = list.stream()
.filter(s -> s.startsWith("A"))
.findFirst()
.map(String::length) // 非空時(shí)執(zhí)行 length()
.orElse(0); // 空時(shí)返回 0flatMap():處理結(jié)果為 Optional 的轉(zhuǎn)換(如關(guān)聯(lián)查詢)。
總結(jié)
stream.findFirst().get() 報(bào)錯(cuò)的本質(zhì)是 “未處理空結(jié)果”,修復(fù)的核心是「用 Optional 提供的安全方法替代直接 get ()」。推薦優(yōu)先級(jí):
- 有默認(rèn)值 →
orElse()/orElseGet(); - 無(wú)結(jié)果是異常 →
orElseThrow(); - 有結(jié)果才執(zhí)行邏輯 →
ifPresent(); - 需復(fù)雜空值處理 →
isPresent()手動(dòng)判空。
到此這篇關(guān)于stream.findFirst().get() 報(bào)錯(cuò) NoSuchElementException的解決方案的文章就介紹到這了,更多相關(guān)stream.findFirst().get() 報(bào)錯(cuò) NoSuchElementException內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解決pycharm py文件運(yùn)行后停止按鈕變成了灰色的問(wèn)題
今天小編就為大家分享一篇解決pycharm py文件運(yùn)行后停止按鈕變成了灰色的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-11-11
Python源碼學(xué)習(xí)之PyType_Type和PyBaseObject_Type詳解
今天給大家?guī)?lái)的是關(guān)于Python源碼的相關(guān)知識(shí)學(xué)習(xí),文章圍繞著PyType_Type和PyBaseObject_Type展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
Python基于面向?qū)ο笞鲆粋€(gè)文件夾整理工具
這篇文章主要給大家介紹了Python基于面向?qū)ο笞鲆粋€(gè)文件夾整理工具,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02
python opencv 檢測(cè)移動(dòng)物體并截圖保存實(shí)例
這篇文章主要介紹了python opencv 檢測(cè)移動(dòng)物體并截圖保存實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Python對(duì)接六大主流數(shù)據(jù)庫(kù)(只需三步)
這篇文章主要介紹了Python對(duì)接六大主流數(shù)據(jù)庫(kù)(只需三步),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
python實(shí)現(xiàn)自動(dòng)獲取IP并發(fā)送到郵箱
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)自動(dòng)獲取IP并發(fā)到郵箱,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
opencv檢測(cè)動(dòng)態(tài)物體的實(shí)現(xiàn)
本文主要介紹了opencv檢測(cè)動(dòng)態(tài)物體的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07

