最近寫一個程序,從文件中讀取一些字/詞,然后存儲起來供別的類引用。
對每個讀入的詞創建一個String,結果許多同樣內容的也會存儲多份,會占用大量內存。
開始的一個想法是使用map/set來存儲所有遇到的詞,這樣就沒有重復的了。然后別的類只需要引用這些詞就可以了。
這樣還需要一些工作量,經別人介紹,發現了String.intern方法。
例如下面代碼
String a = new String("abc"); String b = new String("abc"); assertNotSame(a, b);
a和b是兩個獨立的對象,它們有各自的存儲。所以not same。
但如果
String a = new String("abc").intern(); String b = new String("abc").intern(); assertSame(a, b);
此時assertSame就會pass。現在a和b其實是一個對象,a == b。它們只是同一個對象的兩個名字。在debug時,可以看到他們的id是一樣的。這樣跟
String a = new String("abc"); String b = a;
assertSame(a, b);
效果是完全一樣的。
intern是一個native的方法,但按照其文檔解釋,應該是JVM維護了一個當前進程曾經出現過的字符串的hash表,在調用intern時,會查詢該表。如果已經存在,則直接返回對該String的引用;如果沒有,則創建一個,並加入到hash中。
在我的代碼中,因為是從文件中讀取的詞,所以只需要在讀取詞后,對每個詞調用下intern再存儲,就可以保證同樣內容的字符串只有一份存儲。更確切的說,只有一個對象。即不僅字符串內容只有一份,其overhead(String類的非內容部分,例如其他成員變量)也只有一份。
另外,如果使用字面量(literal)來定義字符串,則自動會調用intern,從而減少內存占用。例如:
String a = new String("abc"); String b = "abc"; String c = "abc"; assertSame(b, c); assertNotSame(a, b);
對於字面量定義的b和c都會自動調用intern,(等用於String b = "abc".intern())。所以此時b和c是指向同一個對象。但a不是字面量定義的,所以是獨立對象。