2011-02-21 48 views
6

有兩個問題。字符串實習和字面字符串聲明的搜索開銷

  1. 當我們聲明字面字符串時,我們搜索堆的字符串池中是否有相同的字符串。 這是否也是實習生(方法實習生String)?

  2. 在我的思想中,每個文字字符串聲明需要一個二進制搜索或東西,所以它的成本至少的log(n)ñ是池中現有弦數。如果池中有很多字符串,可能會造成很高的成本。 (也許權衡搜索成本和內存?)從這個角度來看,聲明字符串可能是危險的。 這個搜索成本有多重要,以及爲什麼用這種方式設計java(當聲明字面字符串時搜索池)。

以下是我參考瞭解的背景。


JavaDoc for the java.lang.String class狀態:

字符串是常數;它們的值在創建後無法更改。字符串緩衝區支持可變字符串。因爲String對象是不可變的,所以它們可以共享。

http://www.janeg.ca/scjp/lang/strLiteral.html評論:

換句話說,因爲編譯器知道字符串原始值不能改變,一旦它創造了它可以安全地使用現有數據和避免重複搞亂內存。

+0

我將您對「JSK 1.3」的引用修改爲官方的JavaDoc。 –

+0

@joachim Sauer謝謝,但最後一句來自你刪除的(http://www.janeg.ca/scjp/lang/strLiteral.html)。你能反映一下嗎?或者我會的。 – RENO

+0

我刪除了它,因爲上面鏈接的JavaDoc是引用的授權原始源,並且該頁的質量有問題(沒有「JSK 1.3」這樣的東西,並且它實際上並沒有鏈接到它的任何源) 。 –

回答

4

您將編譯時間複雜性與運行時複雜性混爲一談。

當這個類被加載時,是的,它做了一個搜索,看看每個文字是否已經存在(儘管我認爲它會使用散列表進行O(1)查找而不是提議)。

代碼運行時,它具有對內存中字符串的引用,因此沒有額外的開銷而非非文字。

所以是的,文字是interned。根據字符串Javadoc,

字符串池,最初是空的,由類String私人維護。

您可以在字符串上調用intern()將其添加到此池中。從邏輯上看,如果a.equals(b)然後a.intern() == b.intern(),因爲.intern()保證返回一個獨特的池。

例子:

class InternTest { 
    // assuming InternTest is the only class, internPool.size = 0 
    String x = "ABC"; // interned at class load, internPool.size = 1 
    String y = "DEF"; // interned at class load, internPool.size = 2 
    String z = "ABC"; // interned at class load, but match found - size = 2 still 

    void foo() { 
     // random int is just a mechanism to get something that I know won't 
     // be interned at loadtime - could have loaded from file or database too 
     int i = (new java.util.Random()).nextInt(1000) + 100; 
     int j = i; 
     String s = String.valueOf(i); // not yet interned, size = 2 still 
     String t = String.valueOf(j); // not yet interned, size = 2 still 

     String sIntern = s.intern(); // manually interned, size = 3 now 
     String tIntern = t.intern(); // manually interned, match found, size = 3 still 

     System.out.println("equals: " + (s.equals(t))); // should be true 
     System.out.println("== raw: " + (s == t)); // should be false, different variables 
     System.out.println("== int: " + (sIntern == tIntern)); // should be true, from unique pool 

     System.out.println("x and z: " + (x == z)); // should be true, interned at class load 
    } 

    public static void main(String[] args) { 
     (new InternTest()).foo(); 
    } 

} 

結果時運行:

C:\Documents and Settings\glowcoder\My Documents>java InternTest 
equals: true 
== raw: false 
== int: true 
x and z: true 

我要指出的是,假設永遠是正確的。 Java語言本身有很多String s,在我們的String有機會看到白天之光之前會被實施。然而,假設一切都按順序加載,如果你只考慮字符串的增量,並且假設沒有與現有實習生髮生衝突(我們都知道實習生可能很挑剔並且充滿戲劇性,對吧?snicker),那麼這些數字確實指示字符串池的大小的增量。

+2

實際上,String interning *不會*在運行時發生(當類被加載時)。但是它只發生一次字符串並且複雜度*是*'O(1)',所以它不是性能問題。 –

+0

當我想到它時,這是有道理的 - 沒有JVM加載,我們將如何保持HashMap的內容?此外,因爲'intern()'是一個本地方法,所以它不能在編譯時完成。我會相應地更新我的答案。謝謝! – corsiKa

+0

感謝您快速回答!我得到了一些進一步的問題。在你的回答中,_一個獨特的pool_,哪一個是唯一的手段:1)池的每個元素是唯一的2)池是唯一的。如果在編譯時有2個元素,然後在運行時聲明第三個字符串沒有實際運行,那麼第三個字符串也是在同一個池中? – RENO

3

1 - 當我們聲明字面字符串時,我們搜索堆的字符串池中是否有相同的字符串。這也是一個實習(String類的方法實習生)?

是的。這個過程被稱爲實習。然而,它發生只是一次 ...當包含文字的類被加載。

2 - 在我看來,每個文字字符串聲明都需要一個二進制搜索或其他東西,所以當n是池中現有字符串的數量時,它至少需要log(n)。

不,它不。該池是一個哈希表。

...並且如果池中有很多串,它可能是高成本的。

不,它不會。查詢字符串池散列表的成本是O(1)

...從這個角度來看,聲明很多文字字符串可能是危險的。

與其他加載成本相比,成本並不顯着,然後JIT編譯類文件。在聲明大量文字字符串時,沒有與性能相關的「危險」。

很明顯,與字符串文字對應的String對象佔用內存「永久」,並且您通常不希望浪費內存不必要。但是如果你需要使用這些常量字符串,它們必須以某種方式表示。而其他表示方式要麼以其他方式使用內存,要麼涉及其他運行時成本;例如從文件中讀取它們或從數據庫中檢索它們的成本。

實習字符串文字的好處是,堆不會與相同文字字符串的多個副本混雜在一起。這對於典型的SE/EE應用來說可能並不重要,但是對於ME平臺堆內存來說是非常重要的,浪費它會是一件壞事。


@RENO詢問字符串被實現的次數。有兩種情況:

  • String.intern()顯式調用的應用程序選擇,使發生,因爲許多(或儘可能少)次。

  • 對於字符串文字,javac編譯器將確保給定的.class文件在其常量池中不包含任何字符串文字的多個副本。這意味着一個在許多地方具有給定文字的類只會導致文字在加載類時被執行一次。但是,如果您有兩個在其各自的源代碼中具有相同文字字符串的類,則它們都將在其各自的常量池中具有字符串值,並且在加載相應的類時都會在字符串中進行實習。

+0

感謝您的好回答!你能否解釋更多關於你的解釋:_是的。這個過程被稱爲實習。但是,它只發生一次......當包含文字的類被加載時._我認爲(實際數量)與(文字字符串的數量+明確的intern()方法的數量)相同。 – RENO

+0

再次感謝!當我讀到你的解釋時,我發現與[Java語言規範](http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#101084),3.10.5例。在鏈接頁面中,(Other.hello == hello)的結果爲true。我無法得到它,因爲你解釋說:_如果你有兩個類與..._,但他們的結果是一樣的。有沒有我錯過的一點? – RENO

+0

@RENO - 我不明白你的困惑。正如JLS解釋的那樣,每個字符串文字都是被實施的。期。我剛剛回答了關於需要調用intern()來實現這一點的**次數**的問題。 –