2013-01-23 13 views
10

Pytanie sprowadza się do tego kodu:Obsługa ciągów znaków Java, co jest gwarantowane?

// setup 
String str1 = "some string"; 
String str2 = new String(str1); 
assert str1.equals(str2); 
assert str1 != str2; 
String str3 = str2.intern(); 

// question cases 
boolean case1 = str1 == "some string"; 
boolean case2 = str1 == str3; 

norma Czy Java daje żadnych gwarantuje o wartości case1 i case2? Link do odpowiedniej części specyfikacji Javy byłby oczywiście niezły.

Tak, sprawdziłem wszystkie "Podobne pytania" znalezione przez SO i nie znalazłem żadnych duplikatów, ponieważ żaden z nich nie odpowiedział na to pytanie w ten sposób. I nie, nie chodzi tu o błędną ideę "optymalizacji" porównań łańcuchów przez zastąpienie equals przez ==.

+2

Jeśli to nie jest bezużyteczna próba optymalizacji, co to jest? –

+0

@dystroy Szczególny przypadek, jaki miałem, dotyczył używania znormalizowanych nazw plików do synchronizacji, a jeśli internowane ciągi są bezpieczne do tego celu, lub jeśli potrzebny jest wspólny "Map fileNameToLockObjectMap". Nie będąc pewnym, skończyłem używając mapy w tym przypadku (i nie wracając do zmiany). – hyde

+0

Blokowanie internowanych łańcuchów nie jest dobrym pomysłem. Jest całkiem prawdopodobne, że będziesz mieć niezamierzone konsekwencje. –

Odpowiedz

13

Oto twój cytat JLS, Section 3.10.5:

Każdy łańcuch jest dosłowne odniesienia (§4.3) do instancji (§4.3.1, §12.5) klasy String (§4.3.3) . Obiekty ciągów mają stałą wartość. Literały łańcuchów - lub, bardziej ogólnie, ciągi, które są wartościami wyrażeń stałych (§ 15.28) - są "internowane", aby udostępniać unikalne instancje , używając metody String.intern.

Zatem program kontrolny składający się z jednostki kompilacji (§7.3)

package testPackage; 
class Test { 
     public static void main(String[] args) { 
       String hello = "Hello", lo = "lo"; 
       System.out.print((hello == "Hello") + " "); 
       System.out.print((Other.hello == hello) + " "); 
       System.out.print((other.Other.hello == hello) + " "); 
       System.out.print((hello == ("Hel"+"lo")) + " "); 
       System.out.print((hello == ("Hel"+lo)) + " "); 
       System.out.println(hello == ("Hel"+lo).intern()); 
     } 
} 

class Other { static String hello = "Hello"; } 

i jednostki kompilacji:

package other; 

public class Other { static String hello = "Hello"; } 

daje wynik: true true true true false true

Przykład ten ilustruje sześć punktów:

tekstowymi w tej samej klasy (§8) w opakowaniu (§7) stanowią odnośniki do tego samego obiektu łańcuchach (§4.3.1).

Dosłowne łańcuchy znaków w różnych klasach w tym samym pakiecie reprezentują odniesienia do tego samego obiektu String.

Łańcuchy literowe w różnych klasach w różnych pakietach podobnie reprezentują odwołania do tego samego obiektu String.

Łańcuchy obliczane za pomocą wyrażeń stałych (§ 15.28) są obliczane w czasie kompilacji , a następnie traktowane tak, jakby były literałami.

Ciągi obliczane przez konkatenację w czasie wykonywania są nowo utworzone i dlatego różne są . Rezultat jawnego powiązania wyliczonego ciągu jest ciągiem takim samym, jak każdy istniejący ciąg literowy z tą samą zawartością, co .

W połączeniu z JavaDoc dla stażysty i masz wystarczającą ilość informacji, aby wydedukować, że obie twoje sprawy zwrócą prawdę.

+0

Jak to działa w środowisku wielowątkowym, np. gdy intern() jest wielokrotnie odbierany z wielu wątków w tym samym czasie. Myślę, że to załatwiło sprawę, ale to tylko domysły. Czy wiesz więcej na ten temat? – Alpedar

+2

@Alpedar - implementacja intern jest natywna i zależna od platformy. Zakładam, że jest bezpieczna dla wątków w oparciu o JavaDoc, ale ich nie ma gwarancji (którą znalazłem) w oficjalnej dokumentacji wspierającej to. – Perception

5

myślę String.intern API dostarcza wystarczających informacji

kałuży strun, początkowo pusta, jest utrzymywane prywatnie przez String klasy.

Gdy wywoływana jest metoda intern, jeśli pula zawiera już ciąg równy temu obiektowi String określonemu przez metodę equals (Object), zwracany jest ciąg z puli. W przeciwnym razie ten obiekt String zostanie dodany do puli i zwrócone zostanie odwołanie do tego obiektu String.

Wynika z tego, że dla dowolnych dwóch ciągów s i t, s.intern() == t.intern() ma wartość true wtedy i tylko wtedy, gdy s.equals (t) jest prawdziwe.

Wszystkie ciągi literowe i wyrażenia stałe o stałej wartości są internowane. Literały łańcuchowe są zdefiniowane w sekcji 3.10.5 specyfikacji języka Java ™.