iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 25
0
Software Development

新手也能懂的 Kotlin Collection 賞玩門道系列 第 25

第二十五天:深入 Collection 核心 - 效能評估

在上一章裡我們提到 Sequence<T> 這個跟 Iterable<T> 用起來很像,但實際行為有點不同的物件。我們可以把 Iterable<T> 想像成是急驚風個性,只要一被呼叫就會馬上把所有資料處理完,也因此中間會產生很多過渡用的 Collection;相對 Sequence<T> 就是位慢郎中,只有在真正要取資料時才會開始動作,而且花的步驟愈少愈好。

在知道這兩種物件的特性後,應該會覺得 Sequence<T> 是個效能比較好的版本?但效能這種事其實沒這麼絕對,所以大家一定會很好奇,到底什麼時候該用哪一個?

在這個章節裡,我們依照 typealias.com 給出的建議整理給大家參考:

考量操作動作的數量

還記得在上一章的說明裡我們提到,Iterable 在執行像 filter()map() 這種操作時,會產生暫時性的 Collection。而當這種暫時性的 Collection 愈多時,也會愈消耗資源。因此,假如你對要資料做很多次的操作的話,那 Sequence 會有比較好的效能。

考量取回元素數量

Sequence<T>Iterable<T> 在處理元素時的順序是不同的,還記得上一章的流程圖裡,Iterable<T> 會把 Collection 裡的所有元素都碰過一遍才執行下一個動作,而 Sequence<T> 則是一次拿一個元素出來,把所有動作執行後,才處理下一個元素。因此,假如取回的元素數量是小量的,那 Sequence<T> 會有比較好的效能。

有許多操作都限制取回元素的數量,像是 take() 可以指定取出的數量;contains()indexOf() 只要定址到元素的位置就不需要再往下找;any()none()find() 只要找到對應的元素後就不需要再往下找;first() 只需要取出第一個,後面的通通都可以跳過。假如 Collection 操作的最後一個動作是以上這些的話,那就改用 Sequence<T> 吧!

考量取回的結果

雖然前面兩個因子都是 Sequence<T> 的效能比較好,但假如處理完資料後要再轉回 Collection 的話,Sequence<T> 的表現就沒這麼理想了。原因是 Iterable<T> 從頭到尾都知道自己的元素有多少個,因此在操作在 ArrayList 的底層時,可以直接分配正確尺寸;反觀 Sequence<T> 因為不知道元素有幾個,所以在取回資料時,要不斷調整 ArrayList 的大小,中間會有複製、分配等大量的底層操作,因此在toList()toSet() 時,效能較差。

所以今天若是不需要考量取回結果的操作像 forEach()Sequence<T> 的效能比較好;但若需取回的類別是 ListSet 的話,則用 Iterable<T> 就可以了。

考量操作的狀態

Sequence<T> 支援的操作大多都是 Stateless 的,而當操作是需要 State 時,Sequence 底層的實作會暫時把 Sequence 轉成 Collection,這過程就會有一些效能的損耗,像是 sortedBy()distinct() 就是這一類的操作。所以,當今天都是 Stateless 的操作像 map()filter() 時,Sequence<T> 有比較好的效能,但若是 Stateful 操作的話,Iterable<T> 有比較好的效能。

考量物件功能

雖然我們都希望程式的效能很好,但開發不是只考慮效能,開發時的方便性及後續的維護也很重要。Sequence<T> 雖然看似是個在許多情境下有較好效能的選項,但相對來說,這個物件身上有的屬性和方法也比較少。比方說 Sequence 因為沒有大小的概念,因此身上不會有 sizeisEmpty() 等方法。甚至 Sequence 因為可以無限大,所以也無法反向取出資料,所以像 RightLast 結尾的 method 都不存在,也無法 slice()reverse()。甚至許多理論操作像 union()intersect() 或是 random() 都沒辦法使用。因此,假如你的資料操作需要這些功能的話,繼續用 Iterable<T>吧!

效能這事很難說個準

在這個章節裡,我們討論了幾個會影響 Sequence<T>Iterable<T> 的因子,了解這些因素可以幫助我們決定何時該使用哪一種物件。不過要注意的是,這些因子也會彼此影響,所以當你的操作有複合因子的時候,到底該用哪個效能比較好,有時也很難說個準。因此在實際開發時,若是對效能有強烈要求,建議拿真實資料來跑 Benchmark 會比較準確。

參考資料


上一篇
第二十四天:深入 Collection 核心 - Sequence
下一篇
第二十六天:深入 Collection 核心 - 泛型
系列文
新手也能懂的 Kotlin Collection 賞玩門道31

尚未有邦友留言

立即登入留言