3 回答

TA貢獻1798條經驗 獲得超3個贊
您的方法恰好有效,因為流管道僅包含無狀態(tài)操作。在這種情況下,順序流評估可能一次處理一個元素,因此對包裝器實例的訪問不會重疊,如此處所示。但請注意,這不是保證的行為。
它絕對不適用于像sorted
和這樣的有狀態(tài)操作distinct
。它也不能用于歸約操作,因為它們總是必須至少保存兩個元素進行處理,其中包括reduce
、min
和max
。在 的情況下collect
,這取決于特定的Collector
。forEachOrdered
由于需要緩沖,因此不適用于并行流。
請注意,即使您使用TheadLocal
創(chuàng)建線程受限包裝器,并行處理也會出現問題,因為無法保證在一個工作線程中創(chuàng)建的對象保持在該線程的本地。工作線程可能會在獲取另一個不相關的工作負載之前將部分結果移交給另一個線程。
所以這個共享的可變包裝器在特定實現的順序執(zhí)行中與一組特定的無狀態(tài)操作一起工作,比如map
, filter
, forEach
, findFirst/Any
, 。all/any/noneMatch
您無法獲得 API 的靈活性,因為您必須限制自己,不能將流傳遞給期望 a 的任意代碼,Stream
也不能使用任意Collector
實現。您也沒有接口的封裝,因為您假設了特定的實現行為。
換句話說,如果你想使用這樣一個可變的包裝器,你最好使用一個實現特定操作的循環(huán)。您確實已經有了這種手動實施的缺點,那么為什么不實施它來獲得優(yōu)勢呢。
另一個要考慮的方面是,你從重用這樣一個可變包裝器中得到了什么。它僅適用于類似循環(huán)的用法,在這種情況下,臨時對象在應用逃逸分析后可能會被優(yōu)化掉。在這種情況下,重用對象、延長它們的生命周期實際上可能會降低性能。
當然,對象標量化不是一種有保證的行為??赡艽嬖谝恍﹫鼍?,例如超過 JVM 內聯限制的長流管道,其中對象不會被刪除。但是,臨時對象并不一定很昂貴。
這已在此答案中進行了解釋。臨時對象的分配成本很低。垃圾收集的主要成本是由仍然存在的對象引起的。這些需要遍歷,并且在為新分配騰出空間時需要移動這些。臨時對象的負面影響是它們可能會縮短垃圾收集輪次之間的時間。但這是分配率和可用分配空間的函數,所以這確實是一個可以通過投入更多 RAM 來解決的問題。更多的 RAM 意味著 GC 周期之間的時間更長,GC 發(fā)生時死對象更多,這使得 GC 的凈成本更小。
盡管如此,避免過度分配臨時對象是一個有效的問題。IntStream
、LongStream
和的存在DoubleStream
表明。但這些是特殊的,因為使用原始類型是使用包裝器對象的可行替代方案,而且沒有重用可變包裝器的缺點。它的不同之處還在于它適用于原始類型和包裝類型在語義上等同的問題。相反,您想解決操作需要包裝器類型的問題。對于原始流也適用,當您需要對象來解決問題時,沒有辦法繞過裝箱,這將為不同的值創(chuàng)建不同的對象,而不是共享可變對象。
因此,如果您同樣遇到一個問題,即存在語義等效的包裝對象避免替代方案而沒有實質性問題,例如在可行Comparator.comparingInt
的情況下使用而不是Comparator.comparing
,您可能仍然會喜歡它。但只有那時。
簡而言之,大多數時候,這種對象重用的節(jié)?。ㄈ绻械脑挘┎⒉荒茏C明其缺點。在特殊情況下,在有益且重要的情況下,使用循環(huán)或完全控制的任何其他構造可能會更好,而不是使用Stream
.

TA貢獻1836條經驗 獲得超3個贊
你可以有一些方便的功能,也可以有線程安全的版本來并行工作。
Function<T,U> threadSafeReusableWrapper(Supplier<U> newWrapperInstanceFn, BiConsumer<U,T> wrapFn) {
final ThreadLocal<T> wrapperStorage = ThreadLocal.withInitial(newWrapperInstanceFn);
return item -> {
T wrapper = wrapperStorage.get();
wrapFn.consume(wrapper, item);
return wrapper;
}
}
Function<T,U> reusableWrapper(U wrapper, BiConsumer<U,T> wrapFn) {
return item -> {
wrapFn.consume(wrapper, item);
return wrapper;
};
}
list.stream()
.map(reusableWrapper(new Wrapper(), Wrapper::setSource))
.forEach( w -> processWrapper(w) );
list.stream()
.map(threadSafeReusableWrapper(Wrapper::new, Wrapper::setSource))
.parallel()
.forEach( w -> processWrapper(w) );
但是,我認為不值得。這些包裝器是短暫的,所以不太可能離開年輕一代,所以會很快被垃圾收集。不過,我認為這個想法值得用微基準庫 JMH檢查

TA貢獻1936條經驗 獲得超7個贊
盡管這是可能的,但引用流外的對象會使代碼在風格上功能性降低。可以使用輔助函數簡單地實現一個封裝得更好的非常接近的等價物:
public class Context {
private static final Wrapper WRAPPER = new Wrapper();
private static void helper(Source source) {
WRAPPER.setSource(source);
processWrapper(WRAPPER);
}
public static void main(String[] args) {
List<Source> list = Arrays.asList(new Source("Foo"), new Source("Baz"), new Source("Bar"));
list.stream().forEach(Context::helper);
}
添加回答
舉報