2020/06/29

[筆記] Effective Java #6 避免創建不必要的物件

Effective Java 3rd 簡體中文版筆記 #6 避免創建不必要的物件
一般來說,最好能重複使用已生成的物件,而不是在每次使用時都重新產生,如果該物件是 immutable,那就生成一次並共享該物件。
// 每一次的 new String("str") 都會產生一個新 String
String s = new String("str"); // X
String s = "str";             // O
要避免開發者生成過多不必要的物件,可以採用的作法有 static factory method [#1],像是在 java.lang.Boolean 中,應優先使用 Boolean.valueOf(String) 而非 new Boolean(String),在 Java 9 中,後者已標示為 @Deprecated
Java 9 Boolean class
https://docs.oracle.com/javase/9/docs/api/java/lang/Boolean.html#Boolean-boolean-
另外如果生成物件的成本非常高,這時就需考慮是否能重用該物件,來提高效率。像是在作字串比對時,生成 Pattern 作比對的效率會比直接使用 String.matches() 高許多。
public boolean isMatch(String s) {
    return s.matches(...);
}

private final Pattern pattern = Pattern.compile(...);
public boolean isMatch(String s) {
    return pattern.matcher(s).matches();
}
在 String.matches 的 source code 中,每次調用便會產生新的 Pattern,但只要 regex 相同,每次生成的都會一樣,所以可以只生成一次,然後將它保留即可。
// java.lang.String source code
public boolean matches(String regex) {
    return Pattern.matches(regex, this);
}

// java.utils.regex.Pattern source code
public static boolean matches(String regex, CharSequence input) {
    Pattern p = Pattern.compile(regex);
    Matcher m = p.matcher(input);
    return m.matches();
}
但並不是每個物件都可以被重複使用,有時候並不容易判斷,像是 adapter 或是稱作 view 的情況,adapter 會定義一介面,而 backing object 會實作該介面。實際的應用像是 Map 中的 keySet(),每次調用 keySet() 回傳的都是同一實體,因為若每次回傳的都是新的 Set,當 Map 發生變化時,無法讓已回傳的 Set 跟著一起變化。
// java.util.HashMap source
transient Set<K> keySet;

public Set<K> keySet() {
    Set<K> ks = keySet;
    if (ks == null) {
        ks = new KeySet();
        keySet = ks;
    }
    return ks;
}
public static void main(String[] args) {
    Map<String, String> map = new HashMap<>();
    map.put("1", "A");
    map.put("2", "B");
    Set<String> keys = map.keySet();
    System.out.println(keys.size());  // 2
    map.put("3", "C");
    System.out.println(keys.size());  // 3
}
另外一種狀況是 autoboxing,autoboxing 的方便性,讓 primitive type 與 boxed primitive type 能混用,兩者在效能上有較明顯差異,所以應優先使用 primitive type 注意是否有無意識的 autoboxing 發生
並不是生成所有物件的成本都是昂貴的,如果要避免生成物件,而使用 object pool 來維護物件,反而是更複雜的,常見會使用 object pool 來管理的,多是像是資料庫的連線池 (connection pool) 等等。

轉載請註明原文網址 https://cookieandcoketw.blogspot.com/2020/06/effective-java-6-unnecessary-object.html

沒有留言:

張貼留言