提取重物件(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()
}
}
透過使用延遲初始化,您可以避免物件創建時的不必要開銷,並提高應用程式的啟動時間。
Uh-oh