一般來說,最好能重複使用已生成的物件,而不是在每次使用時都重新產生,如果該物件是 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另外如果生成物件的成本非常高,這時就需考慮是否能重用該物件,來提高效率。像是在作字串比對時,生成 Pattern 作比對的效率會比直接使用 String.matches() 高許多。
https://docs.oracle.com/javase/9/docs/api/java/lang/Boolean.html#Boolean-boolean-
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
沒有留言:
張貼留言