如果你使用了這個(gè)Stream流操作,升級(jí)Java17有可能會(huì)出問(wèn)題
在Java 8 中,甚至到Java 16 中執(zhí)行下面的Stream流操作
- Stream.of(1, 2, 3, 4)
- .skip(1)
- .limit(2)
- .peek(System.out::println)
- .count();
都會(huì)跳過(guò)元素1,打印元素2以及3,最終計(jì)數(shù)為2,我想大家對(duì)此應(yīng)該都沒(méi)有異議。
但是從Java 17 開(kāi)始,再次執(zhí)行上面的代碼,跳過(guò)元素1,計(jì)數(shù)為2。等等…… 是不是少執(zhí)行了點(diǎn)什么?
是的,不打印元素`2`和`3`了?
從 API 使用的角度來(lái)看,這不太正常。如果我調(diào)用一個(gè)方法,我肯定希望它能夠執(zhí)行,即使它可能拋出一個(gè)異常,但是在這里卻什么也沒(méi)發(fā)生。
- 是的,不打印元素`2`和`3`了?
This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline。
這是對(duì)Stream的peek(Consumer)方法的一個(gè)說(shuō)明,大意是:雖然我們可以在流中通過(guò)peek執(zhí)行一些利用中間操作消費(fèi)元素的方法,胖哥為此還寫(xiě)過(guò)相關(guān)的文章。不過(guò)這個(gè)API的本意設(shè)計(jì)并不是為了改變Stream流中元素的中間態(tài),而是為了Debug,為了讓你能夠觀察到管道中的元素途經(jīng)的點(diǎn):
- Stream.of("one", "two", "three", "four")
- .filter(e -> e.length() > 3)
- // 觀察正在被長(zhǎng)度大于3規(guī)則過(guò)濾的元素
- .peek(e -> System.out.println("Filtered value: " + e))
- .map(String::toUpperCase)
- // 觀察正在被轉(zhuǎn)大寫(xiě)的元素
- .peek(e -> System.out.println("Mapped value: " + e))
- .collect(Collectors.toList());
也就是說(shuō)使用peek()改變Stream元素是在Debug中“副作用”的一個(gè)操作。
Stream流的大小在執(zhí)行跳過(guò)操作skip(n)和限制長(zhǎng)度操作limit(n)后,流的大小長(zhǎng)度是已經(jīng)預(yù)知的,為了獲得流的大小沒(méi)必要去遍歷流的元素,跳過(guò)了遍歷就不能再通過(guò)peek()觀察元素了。
允許流不執(zhí)行對(duì)結(jié)果沒(méi)有任何作用的操作,例如排序一個(gè)已經(jīng)排序的流。這個(gè)操作的結(jié)果是已知的,不需要迭代元素,也不會(huì)影響結(jié)果,所以不迭代。所以不具有觀察(peek)的價(jià)值。
我敢說(shuō)會(huì)有大量的項(xiàng)目、甚至是優(yōu)秀的開(kāi)源項(xiàng)目會(huì)受到這個(gè)新機(jī)制的影響,胖哥也在項(xiàng)目中使用了圖片。所以如果你看到這篇文章而且使用peek()做了一些“副作用”操作,就需要評(píng)估升級(jí)Java17帶來(lái)的影響了。
消息來(lái)源
這一新機(jī)制是Java Champion、Jetbrains核心開(kāi)發(fā)者塔吉爾·瓦列夫(Tagir Valeev)和Oracle Java 語(yǔ)言架構(gòu)師Brian Goetz在一場(chǎng)技術(shù)討論中提及的。
Brian Goetz
那么JDK給的建議是什么
盡量不使用count(),甚至Stream.collect(Collectors.counting())也少用,如果你想改變?cè)兀鶕?jù)情況使用map操作或者foreach操作。如果你在20天后Java17發(fā)布后進(jìn)行升級(jí)一定要注意這一點(diǎn)。不過(guò)說(shuō)實(shí)話peek()用著挺爽的,這么改的話有點(diǎn)可惜了,不知道你對(duì)此有什么看法,歡迎留言討論。