Odpowiedzi:
Różnica zmienności:
String
jest niezmienna , jeśli starają się zmieniać swoje wartości, inny obiekt zostanie utworzony, natomiast StringBuffer
i StringBuilder
są zmienne , aby mogli zmienić ich wartości.
Różnica w zabezpieczeniu gwintu:
Różnica między StringBuffer
i StringBuilder
jest StringBuffer
bezpieczna dla wątków. Więc jeśli aplikacja musi być uruchomiona tylko w jednym wątku, lepiej jest użyć StringBuilder
. StringBuilder
jest bardziej wydajny niż StringBuffer
.
Sytuacje:
String
obiekt jest niezmienny.StringBuilder
jest wystarczające.StringBuffer
ponieważ StringBuffer
jest synchroniczne, aby zapewnić bezpieczeństwo wątków.Strings
kiedy zmienić wartość tworzona jest kolejnym celem. Czy stare odwołanie do obiektu jest unieważniane, tak aby mogło być śmieciami gromadzonymi przez, GC
czy może nawet śmieciami?
String
z StringBuilder
?
String
gdy niezmienna struktura jest odpowiednia; uzyskanie nowej sekwencji znaków z a String
może pociągać za sobą niedopuszczalne obniżenie wydajności, zarówno w czasie pracy procesora, jak i pamięci (uzyskiwanie podciągów jest wydajne procesorowo, ponieważ dane nie są kopiowane, ale oznacza to, że potencjalnie znacznie większa ilość danych może pozostać przydzielona).StringBuilder
gdy chcesz utworzyć zmienną sekwencję znaków, zwykle do połączenia kilku sekwencji znaków razem.StringBuffer
w tych samych okolicznościach, których używałbyś StringBuilder
, ale gdy zmiany w łańcuchu bazowym muszą być zsynchronizowane (ponieważ kilka wątków odczytuje / modyfikuje bufor łańcucha).Zobacz przykład tutaj .
Podstawy:
String
to niezmienna klasa, której nie można zmienić.
StringBuilder
to zmienna klasa, do której można dołączać, zamieniać lub usuwać znaki i ostatecznie przekonwertować na a String
StringBuffer
jest oryginalną zsynchronizowaną wersjąStringBuilder
Powinieneś preferować StringBuilder
we wszystkich przypadkach, w których tylko jeden wątek uzyskuje dostęp do obiektu.
Szczegóły:
Zauważ też, że StringBuilder/Buffers
to nie magia, po prostu używają Array jako obiektu wspierającego i że Array musi zostać ponownie przydzielony, kiedy tylko się zapełni. Upewnij się i stwórz swoje StringBuilder/Buffer
obiekty wystarczająco duże, aby nie musiały być ciągle zmieniane przy każdym .append()
wywołaniu.
Zmiana rozmiaru może stać się bardzo zdegenerowana. Zasadniczo zmienia rozmiar macierzy podkładu na 2-krotność jego obecnej wielkości za każdym razem, gdy trzeba ją rozszerzyć. Może to spowodować przydzielenie dużej ilości pamięci RAM i niewykorzystanie jej, gdy StringBuilder/Buffer
klasy zaczną się powiększać.
W Javie String x = "A" + "B";
stosuje się StringBuilder
za kulisami. W prostych przypadkach nie ma korzyści z deklarowania własnego. Ale jeśli String
budujesz duże obiekty, powiedzmy mniej niż 4k, to deklarowanie StringBuilder sb = StringBuilder(4096);
jest znacznie wydajniejsze niż konkatenacja lub użycie domyślnego konstruktora, który ma tylko 16 znaków. Jeśli masz String
mniej niż 10k, zainicjuj go konstruktorem na 10k, aby być bezpiecznym. Ale jeśli zostanie zainicjowany do 10k, to napiszesz 1 znak więcej niż 10k, zostanie on ponownie przydzielony i skopiowany do tablicy 20k. Tak więc inicjowanie wysokiego jest lepsze niż niskie.
W przypadku automatycznej zmiany rozmiaru przy 17. znaku macierz zastępcza zostaje ponownie przydzielona i skopiowana do 32 znaków, przy 33 znaku to się dzieje ponownie i można ponownie przydzielić i skopiować tablicę na 64 znaki. Możesz zobaczyć, jak to się degeneruje do wielu ponownych alokacji i kopii, których tak naprawdę starasz się unikać StringBuilder/Buffer
.
Pochodzi z kodu źródłowego JDK 6 dla AbstractStringBuilder
void expandCapacity(int minimumCapacity) {
int newCapacity = (value.length + 1) * 2;
if (newCapacity < 0) {
newCapacity = Integer.MAX_VALUE;
} else if (minimumCapacity > newCapacity) {
newCapacity = minimumCapacity;
}
value = Arrays.copyOf(value, newCapacity);
}
Najlepszą praktyką jest zainicjowanie StringBuilder/Buffer
nieco większego, niż myślisz, że będziesz potrzebować, jeśli nie wiesz od razu, jak duży String
będzie, ale możesz się domyślić. Jeden przydział nieco więcej pamięci niż potrzebujesz będzie lepszy niż wiele ponownych przydziałów i kopii.
Uważaj również na zainicjowanie znaku StringBuilder/Buffer
a, String
ponieważ przydzieli tylko rozmiar ciągu + 16 znaków, co w większości przypadków po prostu rozpocznie zdegenerowaną ponowną alokację i cykl kopiowania, którego próbujesz uniknąć. Poniższy kod pochodzi bezpośrednio z kodu źródłowego Java 6.
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
Jeśli przypadkiem trafisz na instancję StringBuilder/Buffer
, której nie utworzyłeś i nie możesz kontrolować wywoływanego konstruktora, istnieje sposób na uniknięcie zdegenerowanego ponownego przydzielania i kopiowania. Zadzwoń .ensureCapacity()
z rozmiarem, w którym chcesz się String
dopasować.
Alternatywy:
Dla przypomnienia, jeśli robisz naprawdę ciężkie String
budowanie i manipulowanie, istnieje o wiele bardziej zorientowana na wydajność alternatywa zwana Liny .
Inną alternatywą jest utworzenie StringList
implementacji poprzez podklasę ArrayList<String>
i dodanie liczników do śledzenia liczby znaków na każdej .append()
i wszystkich operacjach mutacji na liście, a następnie przesłonięcie, .toString()
aby utworzyć StringBuilder
dokładnie taki rozmiar, jakiego potrzebujesz i zapętlić listę i budować wyjściowy, możesz nawet uczynić tę StringBuilder
instancję zmienną i „buforować” wyniki .toString()
i tylko ponownie ją wygenerować, gdy coś się zmieni.
Nie zapominaj również o String.format()
budowaniu stałych sformatowanych danych wyjściowych, które mogą być zoptymalizowane przez kompilator, ponieważ są lepsze.
String x = "A" + "B";
naprawdę kompiluje się, aby być StringBuilder? Dlaczego nie miałby po prostu się skompilować String x = "AB";
, powinien używać StringBuilder tylko wtedy, gdy składniki nie są znane w czasie kompilacji.
Masz na myśli konkatenację?
Przykład ze świata rzeczywistego: chcesz utworzyć nowy ciąg z wielu innych .
Na przykład, aby wysłać wiadomość:
Strunowy
String s = "Dear " + user.name + "<br>" +
" I saw your profile and got interested in you.<br>" +
" I'm " + user.age + "yrs. old too"
StringBuilder
String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" )
.append(" I saw your profile and got interested in you.<br>")
.append(" I'm " ).append( user.age ).append( "yrs. old too")
.toString()
Lub
String s = new StringBuilder(100).appe..... etc. ...
// The difference is a size of 100 will be allocated upfront as fuzzy lollipop points out.
StringBuffer (składnia jest dokładnie taka sama jak w StringBuilder, efekty różnią się)
O
StringBuffer
vs. StringBuilder
Pierwsza jest zsynchronizowana, a później nie.
Tak więc, jeśli wywołasz ją kilka razy w jednym wątku (co stanowi 90% przypadków), StringBuilder
będzie działać znacznie szybciej, ponieważ nie przestanie sprawdzać, czy jest właścicielem blokady wątku.
Dlatego zaleca się stosowanie StringBuilder
(chyba że masz więcej niż jeden wątek uzyskujący dostęp do niego w tym samym czasie, co jest rzadkie)
String
konkatenacja ( przy użyciu operatora + ) może być zoptymalizowana przez kompilator do użycia StringBuilder
pod spodem, więc nie trzeba się już martwić, w dawnych czasach Java było to coś, o czym wszyscy mówią, że należy unikać za wszelką cenę, ponieważ każdą konkatenację utworzył nowy obiekt String. Współczesne kompilatory już tego nie robią, ale nadal dobrą praktyką jest używanie ich StringBuilder
na wypadek, gdybyś używał „starego” kompilatora.
edytować
Dla kogo ciekawi, to właśnie robi kompilator dla tej klasy:
class StringConcatenation {
int x;
String literal = "Value is" + x;
String builder = new StringBuilder().append("Value is").append(x).toString();
}
javap -c StringConcatenation
Compiled from "StringConcatenation.java"
class StringConcatenation extends java.lang.Object{
int x;
java.lang.String literal;
java.lang.String builder;
StringConcatenation();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: new #2; //class java/lang/StringBuilder
8: dup
9: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V
12: ldc #4; //String Value is
14: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_0
18: getfield #6; //Field x:I
21: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
24: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: putfield #9; //Field literal:Ljava/lang/String;
30: aload_0
31: new #2; //class java/lang/StringBuilder
34: dup
35: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V
38: ldc #4; //String Value is
40: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: aload_0
44: getfield #6; //Field x:I
47: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
50: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
53: putfield #10; //Field builder:Ljava/lang/String;
56: return
}
Linie ponumerowane 5–27 dotyczą ciągu o nazwie „dosłowny”
Linie o numerach 31–53 dotyczą ciągu o nazwie „konstruktor”
Nie ma różnicy, dokładnie ten sam kod jest wykonywany dla obu łańcuchów.
StringBuilder
konkatenacji ciągów po prawej stronie zadania. Każda dobra implementacja użyje StringBuilder za kulisami, jak mówisz. Co więcej, twój przykład "a" + "b"
zostałby skompilowany w jeden literał, "ab"
ale jeśli go użyjesz StringBuilder
, spowoduje to dwa niepotrzebne wywołania append()
.
"a"+"b"
ale żeby powiedzieć, co było połączeniem String , zmieniam to, aby było jawne. Nie mówisz, dlaczego nie jest dobrą praktyką. Właśnie to robi (nowoczesny) kompilator. @fuzzy, zgadzam się, zwłaszcza gdy wiesz, jaki byłby końcowy rozmiar łańcucha (w przybliżeniu).
-------------------------------------------------- -------------------------------- String StringBuffer StringBuilder -------------------------------------------------- -------------------------------- Miejsce przechowywania | Kupa sterty puli ciągów strunowych Modyfikowalne | Nie (niezmienne) Tak (zmienne) Tak (zmienne) Bezpieczny wątek | Tak Tak Nie Wydajność | Szybko Bardzo wolno Szybko -------------------------------------------------- --------------------------------
synchronised
i dlatego .
Strunowy
String class
Reprezentuje ciągi znaków. Wszystkie literały łańcuchowe w programie Java, takie jak "abc"
zaimplementowane jako instancje tej klasy.
Obiekty łańcuchowe są niezmienne po ich utworzeniu, którego nie możemy zmienić. ( Ciągi są stałymi )
Jeśli ciąg jest tworzony za pomocą konstruktora lub metody to te ciągi będą przechowywane w pamięci sterty , jak również SringConstantPool
. Ale przed zapisaniem w puli wywołuje intern()
metodę sprawdzania dostępności obiektu o tej samej zawartości w puli przy użyciu metody równości. Jeśli kopia ciągów jest dostępna w puli, zwraca odwołanie. W przeciwnym razie obiekt String zostanie dodany do puli i zwróci odwołanie.
+
) oraz konwersji innych obiektów na ciągi. Łączenie ciągów jest realizowane za pośrednictwem klasy StringBuilder (lub StringBuffer) i jej metody dołączania.String heapSCP = new String("Yash");
heapSCP.concat(".");
heapSCP = heapSCP + "M";
heapSCP = heapSCP + 777;
// For Example: String Source Code
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
Literały łańcuchowe są przechowywane w StringConstantPool
.
String onlyPool = "Yash";
StringBuilder i StringBuffer to zmienna sekwencja znaków. Oznacza to, że można zmienić wartość tych obiektów. StringBuffer ma te same metody co StringBuilder, ale każda metoda w StringBuffer jest zsynchronizowana, więc jest bezpieczna dla wątków.
Dane StringBuffer i StringBuilder można tworzyć tylko przy użyciu nowego operatora. Są więc przechowywane w pamięci sterty.
Instancje StringBuilder nie są bezpieczne dla wielu wątków. Jeśli taka synchronizacja jest wymagana, zaleca się użycie StringBuffer.
StringBuffer threadSafe = new StringBuffer("Yash");
threadSafe.append(".M");
threadSafe.toString();
StringBuilder nonSync = new StringBuilder("Yash");
nonSync.append(".M");
nonSync.toString();
StringBuffer i StringBuilder wywierają metod specjalnych, takich jak.,
replace(int start, int end, String str)
I reverse()
.
UWAGA : StringBuffer i SringBuilder są mutowalne, ponieważ zapewniają implementację
Appendable Interface
.
Kiedy stosować który.
Jeśli nie zamierzasz zmieniać wartości za każdym razem, lepiej użyć String Class
. W ramach Generics, jeśli chcesz posortować Comparable<T>
lub porównać wartości, przejdź do String Class
.
//ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
Set<StringBuffer> set = new TreeSet<StringBuffer>();
set.add( threadSafe );
System.out.println("Set : "+ set);
Jeśli zamierzasz modyfikować wartość za każdym razem, gdy korzystasz z StringBuilder, który jest szybszy niż StringBuffer. Jeśli wiele wątków modyfikuje wartość, przejdź do StringBuffer.
Ponadto StringBuffer
jest bezpieczny dla wątków, co StringBuilder
nie jest.
Tak więc w sytuacji czasu rzeczywistego, gdy różne wątki uzyskują do niego dostęp, StringBuilder
może mieć nieokreślony skutek.
Pamiętaj, że jeśli używasz Java 5 lub nowszej, powinieneś użyć StringBuilder
zamiast StringBuffer
. Z dokumentacji API:
Jako uwalniania JDK 5, do tej klasy zostały uzupełnione o podobnym poziomie zaprojektowane do użytku przez jedną gwintu
StringBuilder
.StringBuilder
Klasa powinna być powszechnie stosowane w preferencji do tego, ponieważ obsługuje wszystkie te same operacje, ale jest szybsze, gdyż nie wykonuje żadnej synchronizacji.
W praktyce prawie nigdy nie użyjesz tego z wielu wątków jednocześnie, więc synchronizacja, StringBuffer
która to robi, prawie zawsze jest niepotrzebnym narzutem.
Osobiście nie sądzę, aby można było z tego skorzystać w prawdziwym świecie StringBuffer
. Kiedy miałbym kiedykolwiek chcieć komunikować się między wieloma wątkami, manipulując sekwencją znaków? To wcale nie brzmi pożytecznie, ale może jeszcze nie widzę światła :)
Różnica między String a pozostałymi dwiema klasami polega na tym, że String jest niezmienny, a pozostałe dwie klasy są zmiennymi.
Ale dlaczego mamy dwie klasy do tego samego celu?
Powodem jest to, że StringBuffer
wątek jest bezpieczny i StringBuilder
nie jest.
StringBuilder
jest nową klasą StringBuffer Api
i został wprowadzony JDK5
i jest zawsze zalecany, jeśli pracujesz w środowisku jednowątkowym, ponieważ jest go dużoFaster
Aby uzyskać szczegółowe informacje, przeczytaj http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/
W języku Java ciąg jest niezmienny. Będąc niezmiennymi, mamy na myśli, że po utworzeniu Łańcucha nie możemy zmienić jego wartości. StringBuffer można modyfikować. Po utworzeniu obiektu StringBuffer po prostu dołączamy treść do wartości obiektu, zamiast tworzyć nowy obiekt. StringBuilder jest podobny do StringBuffer, ale nie jest bezpieczny dla wątków. Metody StingBuilder nie są zsynchronizowane, ale w porównaniu do innych Ciągów, Konstruktor Ciągów działa najszybciej. Różnicę między String, StringBuilder i StringBuffer można poznać , implementując je.