Effective Java Item 47:回傳元素序列時優先使用 Collection 而非 Stream
整理 Effective Java 書中 Item 47: Prefer Collection to Stream 心得筆記
主旨:回傳元素序列時,應優先使用 Collection
Java 8 推出 streams 之後,我們在設計方法時有了新的回傳型別選擇。不過,若你要回傳一串元素,Collection
或其子型別通常仍是最合適的選擇,因為這樣使用者不論是想用 for-each 迴圈還是用 stream pipeline,都能方便操作。除非真的有特殊理由,否則不要只回傳 Stream。
點出問題
在 Java 8 以前,回傳元素序列的常見選擇有:
Collection
,Set
,List
:最常見也最好用的泛型集合介面Iterable
:當不需要支援contains()
等 Collection 方法時- 陣列:當元素為原始型別或有極高效能需求時
Java 8 加入 Stream 後,很多人以為回傳 Stream 就是新潮正確的做法。但其實 Stream 並沒有取代 iteration,如果你只回傳 Stream,會讓想用 for-each 的使用者非常困擾,因為 Stream
並沒有繼承 Iterable
,無法直接用 for-each。
範例:回傳 Stream 的迴圈要寫得很噁心
雖然可以靠 adapter 轉型讓 Stream 能用 for-each,但很不直覺又難讀:
比較好的做法是自己寫個 adapter function:
相反地,如果你 API 回傳的是 Iterable
,但使用者想用 stream pipeline,也會卡住。這時候也可以自己寫 adapter:
小結論:用 Collection 最萬用
既然 Collection
本身就繼承 Iterable
,又有 .stream()
方法可轉成 Stream,回傳 Collection
幾乎可以滿足所有需求。像這樣:
真實世界設計案例
1. 回傳一份排行榜前 10 名使用者清單
假設你要寫一個 API,回傳使用者排行榜前十名。這時候資料量不多,也已經整理好,可以直接用 List 回傳:
使用者可以用 for-each 直接列印,或 .stream() 轉換成 stream 做進一步處理,彈性高又易懂:
2. 資料量很大時,就不該用 Collection
如果你要提供一個方法,列出 1 到 10 億的整數,這時候若用 List 就會爆記憶體,應該用 Stream:
使用者可以用 .limit() 取前幾筆資料,或是 .filter() 找出特定條件的數字,不會一次塞爆記憶體:
小結
- 公開 API 若要回傳元素序列,優先選擇 Collection 或其子型別,能同時支援 for-each 與 stream。
- 若有記憶體或效能考量,可考慮:
- 自行實作 Collection
- 或直接回傳 Stream / Iterable,但最好提供雙版本方法
Stream
尚未繼承Iterable
,但若未來有改,則可以只回傳 Stream,並兼顧兩者需求。
簡單一句話總結:Collection 是最通用的回傳型別,能滿足最多用戶端需求。