Odpowiedzi:
Różnica zmienności:
Stringjest niezmienna , jeśli starają się zmieniać swoje wartości, inny obiekt zostanie utworzony, natomiast StringBufferi StringBuildersą zmienne , aby mogli zmienić ich wartości.
Różnica w zabezpieczeniu gwintu:
Różnica między StringBufferi StringBuilderjest StringBufferbezpieczna dla wątków. Więc jeśli aplikacja musi być uruchomiona tylko w jednym wątku, lepiej jest użyć StringBuilder. StringBuilderjest bardziej wydajny niż StringBuffer.
Sytuacje:
Stringobiekt jest niezmienny.StringBuilderjest wystarczające.StringBufferponieważ StringBufferjest synchroniczne, aby zapewnić bezpieczeństwo wątków.Stringskiedy zmienić wartość tworzona jest kolejnym celem. Czy stare odwołanie do obiektu jest unieważniane, tak aby mogło być śmieciami gromadzonymi przez, GCczy może nawet śmieciami?
Stringz StringBuilder?
Stringgdy niezmienna struktura jest odpowiednia; uzyskanie nowej sekwencji znaków z a Stringmoż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).StringBuildergdy chcesz utworzyć zmienną sekwencję znaków, zwykle do połączenia kilku sekwencji znaków razem.StringBufferw 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:
Stringto niezmienna klasa, której nie można zmienić.
StringBuilderto zmienna klasa, do której można dołączać, zamieniać lub usuwać znaki i ostatecznie przekonwertować na a String
StringBufferjest oryginalną zsynchronizowaną wersjąStringBuilder
Powinieneś preferować StringBuilderwe wszystkich przypadkach, w których tylko jeden wątek uzyskuje dostęp do obiektu.
Szczegóły:
Zauważ też, że StringBuilder/Buffersto 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/Bufferobiekty 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/Bufferklasy zaczną się powiększać.
W Javie String x = "A" + "B";stosuje się StringBuilderza kulisami. W prostych przypadkach nie ma korzyści z deklarowania własnego. Ale jeśli Stringbudujesz 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 Stringmniej 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/Buffernieco większego, niż myślisz, że będziesz potrzebować, jeśli nie wiesz od razu, jak duży Stringbę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/Buffera, Stringponieważ 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ę Stringdopasować.
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 StringListimplementacji 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ć StringBuilderdokładnie taki rozmiar, jakiego potrzebujesz i zapętlić listę i budować wyjściowy, możesz nawet uczynić tę StringBuilderinstancję 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), StringBuilderbę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)
Stringkonkatenacja ( przy użyciu operatora + ) może być zoptymalizowana przez kompilator do użycia StringBuilderpod 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 StringBuilderna 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.
StringBuilderkonkatenacji 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
-------------------------------------------------- --------------------------------
synchronisedi dlatego .
Strunowy
String classReprezentuje 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 StringBufferjest bezpieczny dla wątków, co StringBuildernie jest.
Tak więc w sytuacji czasu rzeczywistego, gdy różne wątki uzyskują do niego dostęp, StringBuildermoże mieć nieokreślony skutek.
Pamiętaj, że jeśli używasz Java 5 lub nowszej, powinieneś użyć StringBuilderzamiast 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.StringBuilderKlasa 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, StringBufferktó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 StringBufferwątek jest bezpieczny i StringBuildernie jest.
StringBuilderjest nową klasą StringBuffer Apii został wprowadzony JDK5i 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.