iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0

重物件挪抬

提取重物件(heavy object lifting)到外部是一個對效能非常有用的技巧。例如,這是一個計算數值等於最大值的數量的函數:

fun <T : Comparable<T>> Iterable<T>.countMax(): Int =
    count { it == this.maxOrNull() }

更好的解決方案是把最大的元素提取到 countMax 函數的層次

fun <T : Comparable<T>> Iterable<T>.countMax(): Int {
    val max = this.maxOrNull()
    return count { it == max }
}

這個解決方案在性能上更好,因為不需要在每個廻圈中找到接收者上的最大元素。這也提高了可讀性,因為很明顯地 max 是是要先決定的,所以它在所有廻圈中都是相同的。

常被忽略的重物件

將值提取到外部,以避免不必要的重新計算,是一個重要的做法。這聽起來可能很容易,但可能常常被忽略。以下這個方法,我們使用正則表達式來確定一個字串是否包含有效的IP地址:


fun String.isValidIpAddress(): Boolean {
    return this.matches(
        ("\\A(?:(?:25[0-5]|2[0-4][0-9]|" +
        "[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|" +
        "[01]?[0-9][0-9]?)\\z").toRegex()
    )
}

// 使用方式
print("5.173.80.254".isValidIpAddress()) // true

這個方法的問題是,每次我們使用它時,都需要創建一個 Regex 物件。這是一個嚴重的缺點,因為正則表達式的編譯是一個複雜的操作。這就是為什麼這個方法不適合在性能受限的程式碼被重複使用。然而,我們可以通過將正則表達式提升到上層來改善它:


private val IS_VALID_IP_REGEX = "\\A(?:(?:25[0-5]|2[0-4]" +
"[0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|" +
"[01]?[0-9][0-9]?)\\z".toRegex()

fun String.isValidIpAddress(): Boolean =
    matches(IS_VALID_IP_REGEX)

如果這個函數和其他一些函數在同一個文件中,而我們不想在被使用前創建這個物件,我們甚至可以延遲初始化正則表達式:

private val IS_VALID_IP_REGEX by lazy {
    ("\\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}" +
    "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\z").toRegex()
}

在處理物件時,使屬性成為延遲初始化也很有用。

延遲初始化

使用延遲初始化可以提高性能,因為它會將屬性的初始化延遲到第一次被訪問時。


class Example {
    val expensiveObject: ExpensiveObject by lazy {
        // 在第一次訪問時,將初始化 expensiveObject。
        ExpensiveObject()
    }
}

透過使用延遲初始化,您可以避免物件創建時的不必要開銷,並提高應用程式的啟動時間。

每日一推(G)I-DLE

Uh-oh

Yes


上一篇
D24: Kotlin 效能 - 減少物件建立與快取
下一篇
D26: Kotlin 效能 - 給函式俄羅斯娃娃使用 inline 修飾子
系列文
讓 Kotlin 程式碼更道地 - 談 Effective Kotlin 與相關的 Design Pattern30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言