iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
Kotlin

讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern系列 第 27

D27: 偏好使⽤ Sequences 來取代大量且有多次操作的集合

  • 分享至 

  • xImage
  •  

Effective Kotlin Item 51: 偏好使⽤ Sequences 來取代巨量且有多次操作⾏為的 Collection

Iterable 與 Sequence

序列(Sequence)是 Kotlin 中的一種類型,提供與其他集合相似的特性,但以惰性和高效的方式處理資料。這種惰性的方法在處理多次操作集合時很有用,特別是在處理大量數據時。

對於一系列的集合操作,使用序列可以更加高效,因為它避免了中間集合的建立。例如,連續的 map 和 filter 操作在 Iterable 上會創建多個中間集合,而在 Sequence 上則不會。

Iterable

  • 每⼀步都回傳⼀個新的 Collection
  • 積極的 (Eager),每⼀步都會觸發運算

Sequence

  • 每⼀步都回傳⼀個新的 Sequence
  • 懶惰的 (Lazy),除非有結束型操作才會開始運算

Sequence 的中間操作與終端操作

在 Kotlin 的 Sequence 中,操作可以分為兩種主要類型:中間操作(intermediate operations)和終端操作(terminal operations)。

中間操作 (Intermediate Operations)

中間操作返回一個新的 Sequence,它描述了該操作的效果,但不實際執行任何操作。中間操作是惰性加載的,這意味著直到有終端操作調用時,操作才會真正執行。
常見的中間操作有:

  • map: 對序列的每個元素進行轉換
  • filter: 篩選出滿足特定條件的元素
  • flatMap: 將每個元素轉換為另一個序列,然後將所有這些序列銜接成一個序列
  • distinct: 去除重複的元素
  • take: 從序列中取前 N 個元素
  • drop: 從序列中丟棄前 N 個元素

終端操作 (Terminal Operations)

終端操作啟動所有前面的中間操作,並產生一個結果或副作用。一旦終端操作被調用,序列的元素就會被消耗,且這個特定的序列不可以再使用。但由於序列是冷的,所以你可以再次建立或重新開始相同的序列。
常見的終端操作有:

  • toList 或 toSet: 將序列轉換為列表或集合
  • forEach: 對序列的每個元素進行某種操作
  • count: 計算序列的元素數
  • first 或 firstOrNull: 獲取序列的第一個元素
  • last 或 lastOrNull: 獲取序列的最後一個元素
  • find: 查找滿足特定條件的第一個元素
  • any, all, none: 根據某個條件檢查序列的元素
    這兩種操作的主要區別在於它們的執行方式:中間操作描述了將要執行的操作,但直到碰到終端操作時才真正執行。而終端操作會啟動序列的實際計算。

示例一

sequence 是一個元素跑到終端操作再換下一個元素。可以看以下執行的不同

sequenceOf(1, 2, 3)
    .filter { print("F$it, "); it % 2 " 1 }
    .map { print("M$it, "); it * 2 }
    .forEach { print("E$it, ") }
// Prints: F1, M1, E2, F2, F3, M3, E6,

listOf(1, 2, 3)
    .filter { print("F$it, "); it % 2 " 1 }
    .map { print("M$it, "); it * 2 }
    .forEach { print("E$it, ") }
// Prints: F1, F2, F3, M1, M3, E2, E6,

以資料特性來分

操作執行的順序也有所不同:序列對每個單獨的元素逐一執行所有的處理步驟。而 Iterable 則是先完成整個集合的每一步,然後再進行下一步。因此,序列讓您避免建立中間步驟的結果,從而提高整個集合處理鏈的性能。但是,序列的惰性加載特性增加了一些開銷,當處理較小的集合或進行更簡單的計算時,這可能會很明顯。因此,您應該考慮 Sequence 和 Iterable,並決定哪一個更適合您的情況。

Iterable: 當你已知資料集大小並且希望立即獲得結果時,使用 Iterable 會更加合適。
Sequence: 當你需要連續地對數據集執行多個操作,或當你工作於可能是無限的或非常大的數據集時,使用 Sequence 會更高效和適當。

使⽤ Sequences 來取代巨量且有多次操作⾏為的集合

多⼤叫巨量?

  • 上萬筆以上的資料
  • 元素本⾝就好幾 MB

多少次叫多次?

  • 超過⼀次
  • 依據數量級的不同才會有感

什麼時候 Sequence 會不快?

-當使⽤ sorted() 的時候

  • 還是得把整個 Collection 跑⼀遍 (底層有做⼀些轉換)
  • 無法⽤在無限⼤的 Sequence 上
  • 但在⼤部份的情況下還是可能比 Collection 快⼀點

怎麼看 Java Stream?

  • Kotlin 的函式比較多且語法比較好⽤
  • Kotlin 的 Sequence 可以多平台使⽤
  • Java 的 Stream 可以啟動 Parallel 模式,可以有很⼤幅度的效能強化,但有些陷阱要注意
  • 不使⽤ Parallel 模式時,很難說 Stream 的效能比 Sequence 好

每日一推(G)-IDLE

即使是同一首歌,每次舞台的服裝也會不同,我們後面來看幾個 stage mix 吧。先來個 Nxde

Yes


上一篇
D26: Kotlin 效能 - 給函式俄羅斯娃娃使用 inline 修飾子
下一篇
D28: Kotlin 集合的效率 - 利用複合高階函式減少操作
系列文
讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言