Jaka jest główna różnica między StringBuffer
i StringBuilder
? Czy przy podejmowaniu decyzji dotyczącej któregokolwiek z nich występują problemy z wydajnością?
Jaka jest główna różnica między StringBuffer
i StringBuilder
? Czy przy podejmowaniu decyzji dotyczącej któregokolwiek z nich występują problemy z wydajnością?
Odpowiedzi:
StringBuffer
jest zsynchronizowany, StringBuilder
nie jest.
StringBuilder
jest szybszy niż StringBuffer
dlatego, że nie jest synchronized
.
Oto prosty test porównawczy:
public class Main {
public static void main(String[] args) {
int N = 77777777;
long t;
{
StringBuffer sb = new StringBuffer();
t = System.currentTimeMillis();
for (int i = N; i --> 0 ;) {
sb.append("");
}
System.out.println(System.currentTimeMillis() - t);
}
{
StringBuilder sb = new StringBuilder();
t = System.currentTimeMillis();
for (int i = N; i > 0 ; i--) {
sb.append("");
}
System.out.println(System.currentTimeMillis() - t);
}
}
}
Test nie daje numery 2241 ms
dla StringBuffer
vs 753 ms
dla StringBuilder
.
--> 0
w pętli. Chwilę zajęło mi zrozumienie, co to znaczy. Czy jest to coś, co faktycznie jest używane w praktyce zamiast zwykłej ...; i > 0; i--
składni?
i -->
jest naprawdę denerwujące pod względem składni ... Na początku myślałem, że to strzała z powodu komentarzy na temat sztuki ASCII.
main()
Ponadto test porównawczy jest niesprawiedliwy. Nie ma rozgrzewki.
Zasadniczo StringBuffer
metody są synchronizowane podczasStringBuilder
nie są.
Operacje są „prawie” takie same, ale stosowanie metod zsynchronizowanych w jednym wątku to przesada.
To właściwie tyle.
Cytat z StringBuilder API :
Ta klasa [StringBuilder] zapewnia API kompatybilne z StringBuffer, ale bez gwarancji synchronizacji . Ta klasa została zaprojektowana do użycia jako zastępczy element zastępujący StringBuffer w miejscach, w których bufor łańcucha był używany przez pojedynczy wątek (jak to zwykle ma miejsce). Tam, gdzie to możliwe, zaleca się stosowanie tej klasy zamiast StringBuffer, ponieważ w większości implementacji będzie ona szybsza.
Więc został stworzony, aby go zastąpić.
To samo stało się z Vector
i ArrayList
.
Hashtable
i HashMap
.
Ale czy chcesz uzyskać wyraźną różnicę za pomocą przykładu?
StringBuffer lub StringBuilder
Po prostu użyj, StringBuilder
chyba że naprawdę próbujesz współdzielić bufor między wątkami. StringBuilder
jest niezsynchronizowanym (mniej narzutu = bardziej wydajnym) młodszym bratem oryginalnej synchronizowanej StringBuffer
klasy.
StringBuffer
był pierwszy. Firma Sun martwiła się o poprawność w każdych warunkach, dlatego zsynchronizowała ją, aby na wszelki wypadek była bezpieczna dla wątków.
StringBuilder
przyszedł później. Większość zastosowańStringBuffer
była jednowątkowa i niepotrzebnie pokrywała koszty synchronizacji.
Ponieważ StringBuilder
jest zastępczym zamiennikiem dlaStringBuffer
bez synchronizacji, nie byłoby żadnych różnic pomiędzy przykładami.
Jeśli są próby akcję pomiędzy wątkami, można użyć StringBuffer
, ale zastanowić się, czy synchronizacja wyższego poziomu jest konieczne, na przykład może zamiast używać StringBuffer, należy zsynchronizować metody, które wykorzystują StringBuilder.
Najpierw zobaczmy podobieństwa : zarówno StringBuilder, jak i StringBuffer można modyfikować. Oznacza to, że możesz zmienić ich zawartość w tej samej lokalizacji.
Różnice : StringBuffer można również modyfikować i synchronizować. Gdzie jako StringBuilder jest zmienny, ale domyślnie nie jest synchronizowany.
Znaczenie synchronizacji (synchronizacja) : Gdy coś jest synchronizowane, wiele wątków może uzyskać dostęp i modyfikować je bez żadnego problemu lub efektu ubocznego. StringBuffer jest zsynchronizowany, więc możesz używać go z wieloma wątkami bez żadnego problemu.
Którego użyć, kiedy? StringBuilder: Gdy potrzebujesz łańcucha, który można modyfikować, a tylko jeden wątek uzyskuje do niego dostęp i go modyfikuje. StringBuffer: Gdy potrzebujesz łańcucha, który można modyfikować, a wiele wątków ma do niego dostęp i go modyfikuje.
Uwaga : Nie używaj StringBuffer niepotrzebnie, tzn. Nie używaj go, jeśli tylko jeden wątek modyfikuje i uzyskuje do niego dostęp, ponieważ ma dużo kodu blokującego i odblokowującego do synchronizacji, który niepotrzebnie zajmuje czas procesora. Nie używaj zamków, chyba że jest to wymagane.
W pojedynczych wątkach StringBuffer nie jest znacznie wolniejszy niż StringBuilder , dzięki optymalizacji JVM. W wielowątkowości nie można bezpiecznie używać StringBuilder.
Oto mój test (nie test, tylko test):
public static void main(String[] args) {
String withString ="";
long t0 = System.currentTimeMillis();
for (int i = 0 ; i < 100000; i++){
withString+="some string";
}
System.out.println("strings:" + (System.currentTimeMillis() - t0));
t0 = System.currentTimeMillis();
StringBuffer buf = new StringBuffer();
for (int i = 0 ; i < 100000; i++){
buf.append("some string");
}
System.out.println("Buffers : "+(System.currentTimeMillis() - t0));
t0 = System.currentTimeMillis();
StringBuilder building = new StringBuilder();
for (int i = 0 ; i < 100000; i++){
building.append("some string");
}
System.out.println("Builder : "+(System.currentTimeMillis() - t0));
}
Wyniki:
łańcuchy: 319740
Bufory: 23
Konstruktor: 7!
Tak więc Konstruktorzy są szybsi niż bufory i DZIŚ szybciej niż konkatenacja łańcuchów. Teraz użyjmy Executora dla wielu wątków:
public class StringsPerf {
public static void main(String[] args) {
ThreadPoolExecutor executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
//With Buffer
StringBuffer buffer = new StringBuffer();
for (int i = 0 ; i < 10; i++){
executorService.execute(new AppendableRunnable(buffer));
}
shutdownAndAwaitTermination(executorService);
System.out.println(" Thread Buffer : "+ AppendableRunnable.time);
//With Builder
AppendableRunnable.time = 0;
executorService = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);
StringBuilder builder = new StringBuilder();
for (int i = 0 ; i < 10; i++){
executorService.execute(new AppendableRunnable(builder));
}
shutdownAndAwaitTermination(executorService);
System.out.println(" Thread Builder: "+ AppendableRunnable.time);
}
static void shutdownAndAwaitTermination(ExecutorService pool) {
pool.shutdown(); // code reduced from Official Javadoc for Executors
try {
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow();
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (Exception e) {}
}
}
class AppendableRunnable<T extends Appendable> implements Runnable {
static long time = 0;
T appendable;
public AppendableRunnable(T appendable){
this.appendable = appendable;
}
@Override
public void run(){
long t0 = System.currentTimeMillis();
for (int j = 0 ; j < 10000 ; j++){
try {
appendable.append("some string");
} catch (IOException e) {}
}
time+=(System.currentTimeMillis() - t0);
}
}
Teraz StringBuffers zajmuje 157 ms na 100000 dołączeń. To nie jest ten sam test, ale w porównaniu z poprzednimi 37 ms można bezpiecznie założyć, że dołączenia StringBuffers są wolniejsze przy użyciu wielowątkowości . Powodem jest to, że JIT / hotspot / kompilator / coś dokonuje optymalizacji, gdy wykryje, że nie ma potrzeby sprawdzania blokad.
Ale dzięki StringBuilder masz java.lang.ArrayIndexOutOfBoundsException , ponieważ współbieżny wątek próbuje dodać coś tam, gdzie nie powinno.
Wniosek jest taki, że nie musisz ścigać StringBuffers. A jeśli masz wątki, zastanów się, co robią, zanim spróbujesz uzyskać kilka nanosekund.
withString+="some string"+i+" ; ";
nie jest to równoważne z pozostałymi dwiema pętlami i dlatego nie jest uczciwym porównaniem.
StringBuilder został wprowadzony w Javie 1.5, więc nie będzie działał z wcześniejszymi JVM.
Z Javadocs :
Klasa StringBuilder zapewnia API kompatybilne z StringBuffer, ale bez gwarancji synchronizacji. Ta klasa została zaprojektowana do użycia jako zastępczy element zastępujący StringBuffer w miejscach, w których bufor łańcucha był używany przez pojedynczy wątek (jak to zwykle ma miejsce). Tam, gdzie to możliwe, zaleca się stosowanie tej klasy zamiast StringBuffer, ponieważ w większości implementacji będzie ona szybsza.
StringBuilder
.
Całkiem dobre pytanie
Oto różnice, które zauważyłem:
StringBuffer: -
StringBuffer is synchronized
StringBuffer is thread-safe
StringBuffer is slow (try to write a sample program and execute it, it will take more time than StringBuilder)
StringBuilder: -
StringBuilder is not synchronized
StringBuilder is not thread-safe
StringBuilder performance is better than StringBuffer.
Zwykła rzecz :-
Oba mają te same metody z tymi samymi podpisami. Oba są zmienne.
StringBuffer
StringBuilder
StringBuffer
bez żadnych innych zmianappend
dwa razy, albo append
i toString
nie, nie jest bezpieczne.
StringBuilder
i StringBuffer
są prawie takie same. Różnica polega na tym, że nie StringBuffer
jest zsynchronizowana StringBuilder
. Chociaż StringBuilder
jest szybszy niż StringBuffer
, różnica w wydajności jest bardzo mała. StringBuilder
jest zamiennikiem SUN dla StringBuffer
. Po prostu unika synchronizacji ze wszystkimi publicznymi metodami. Zamiast tego ich funkcjonalność jest taka sama.
Przykład dobrego użytkowania:
Jeśli twój tekst ma się zmienić i jest używany przez wiele wątków, lepiej użyć tego StringBuffer
. Jeśli twój tekst ma się zmienić, ale jest używany przez jeden wątek, użyj StringBuilder
.
StringBuffer
StringBuffer można modyfikować, co oznacza, że można zmienić wartość obiektu. Obiekt utworzony za pomocą StringBuffer jest przechowywany w stercie. StringBuffer ma takie same metody jak StringBuilder, ale każda metoda w StringBuffer jest zsynchronizowana, a StringBuffer jest bezpieczny dla wątków.
z tego powodu nie pozwala dwóm wątkom na jednoczesny dostęp do tej samej metody. Dostęp do każdej metody może mieć jeden wątek na raz.
Ale bycie bezpiecznym dla wątków ma również wady, ponieważ wydajność uderzeń StringBuffer wynika z właściwości bezpiecznych dla wątków. Zatem StringBuilder jest szybszy niż StringBuffer podczas wywoływania tych samych metod dla każdej klasy.
Wartość StringBuffer można zmienić, co oznacza, że można ją przypisać do nowej wartości. W dzisiejszych czasach jest to najczęściej zadawane pytanie dotyczące wywiadu, różnice między powyższymi klasami. Bufor ciągów można przekonwertować na ciąg przy użyciu metody toString ().
StringBuffer demo1 = new StringBuffer(“Hello”) ;
// The above object stored in heap and its value can be changed .
demo1=new StringBuffer(“Bye”);
// Above statement is right as it modifies the value which is allowed in the StringBuffer
StringBuilder
StringBuilder jest taki sam jak StringBuffer, tzn. Przechowuje obiekt w stercie i można go również modyfikować. Główną różnicą między StringBuffer i StringBuilder jest to, że StringBuilder również nie jest bezpieczny dla wątków. StringBuilder jest szybki, ponieważ nie jest bezpieczny dla wątków.
StringBuilder demo2= new StringBuilder(“Hello”);
// The above object too is stored in the heap and its value can be modified
demo2=new StringBuilder(“Bye”);
// Above statement is right as it modifies the value which is allowed in the StringBuilder
String
jest niezmienny.
StringBuffer
jest zmienny i zsynchronizowany.
StringBuilder
jest również zmienny, ale nie jest zsynchronizowany.
Javadoc wyjaśnia różnicę:
Ta klasa zapewnia API kompatybilne z StringBuffer, ale bez gwarancji synchronizacji. Ta klasa została zaprojektowana do użycia jako zastępczy element zastępujący StringBuffer w miejscach, w których bufor łańcucha był używany przez pojedynczy wątek (jak to zwykle ma miejsce). Tam, gdzie to możliwe, zaleca się stosowanie tej klasy zamiast StringBuffer, ponieważ w większości implementacji będzie ona szybsza.
StringBuilder
(wprowadzony w Javie 5) jest identyczny z StringBuffer
, ale jego metody nie są zsynchronizowane. Oznacza to, że ma lepszą wydajność niż ta ostatnia, ale wadą jest to, że nie jest bezpieczna dla wątków.
Przeczytaj samouczek, aby uzyskać więcej informacji.
Prosty program ilustrujący różnicę między StringBuffer a StringBuilder:
/**
* Run this program a couple of times. We see that the StringBuilder does not
* give us reliable results because its methods are not thread-safe as compared
* to StringBuffer.
*
* For example, the single append in StringBuffer is thread-safe, i.e.
* only one thread can call append() at any time and would finish writing
* back to memory one at a time. In contrast, the append() in the StringBuilder
* class can be called concurrently by many threads, so the final size of the
* StringBuilder is sometimes less than expected.
*
*/
public class StringBufferVSStringBuilder {
public static void main(String[] args) throws InterruptedException {
int n = 10;
//*************************String Builder Test*******************************//
StringBuilder sb = new StringBuilder();
StringBuilderTest[] builderThreads = new StringBuilderTest[n];
for (int i = 0; i < n; i++) {
builderThreads[i] = new StringBuilderTest(sb);
}
for (int i = 0; i < n; i++) {
builderThreads[i].start();
}
for (int i = 0; i < n; i++) {
builderThreads[i].join();
}
System.out.println("StringBuilderTest: Expected result is 1000; got " + sb.length());
//*************************String Buffer Test*******************************//
StringBuffer sb2 = new StringBuffer();
StringBufferTest[] bufferThreads = new StringBufferTest[n];
for (int i = 0; i < n; i++) {
bufferThreads[i] = new StringBufferTest(sb2);
}
for (int i = 0; i < n; i++) {
bufferThreads[i].start();
}
for (int i = 0; i < n; i++) {
bufferThreads[i].join();
}
System.out.println("StringBufferTest: Expected result is 1000; got " + sb2.length());
}
}
// Every run would attempt to append 100 "A"s to the StringBuilder.
class StringBuilderTest extends Thread {
StringBuilder sb;
public StringBuilderTest (StringBuilder sb) {
this.sb = sb;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
sb.append("A");
}
}
}
//Every run would attempt to append 100 "A"s to the StringBuffer.
class StringBufferTest extends Thread {
StringBuffer sb2;
public StringBufferTest (StringBuffer sb2) {
this.sb2 = sb2;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
sb2.append("A");
}
}
}
StringBuffer służy do przechowywania ciągów znaków, które zostaną zmienione (obiektów String nie można zmienić). Automatycznie rozszerza się w razie potrzeby. Powiązane klasy: String, CharSequence.
StringBuilder został dodany w Javie 5. Jest pod każdym względem identyczny z StringBuffer, z wyjątkiem tego, że nie jest zsynchronizowany, co oznacza, że jeśli wiele wątków uzyskuje dostęp do niego w tym samym czasie, mogą wystąpić problemy. W przypadku programów jednowątkowych najczęstszy przypadek, unikając narzutu związanego z synchronizacją, powoduje, że StringBuilder jest nieco szybszy.
StringBuilder
zwykle są lokalne dla metody, w której są widoczne tylko dla jednego wątku.
StringBuffer
jest zsynchronizowany, ale StringBuilder
nie jest. W rezultacie StringBuilder
jest szybszy niż StringBuffer
.
StringBuffer można modyfikować. Może się zmieniać pod względem długości i zawartości. StringBuffer są bezpieczne dla wątków, co oznacza, że mają zsynchronizowane metody kontroli dostępu, dzięki czemu tylko jeden wątek może uzyskać dostęp do zsynchronizowanego kodu obiektu StringBuffer na raz. Tak więc obiektów StringBuffer można ogólnie bezpiecznie używać w środowisku wielowątkowym, w którym wiele wątków może próbować uzyskać dostęp do tego samego obiektu StringBuffer w tym samym czasie.
StringBuilder Klasa StringBuilder jest bardzo podobna do StringBuffer, z tym wyjątkiem, że jej dostęp nie jest zsynchronizowany, więc nie jest bezpieczny wątkowo. Bez synchronizacji wydajność StringBuilder może być lepsza niż StringBuffer. Dlatego jeśli pracujesz w środowisku jednowątkowym, użycie StringBuilder zamiast StringBuffer może spowodować zwiększenie wydajności. Dotyczy to również innych sytuacji, takich jak zmienna lokalna StringBuilder (tj. Zmienna w metodzie), w której tylko jeden wątek będzie uzyskiwał dostęp do obiektu StringBuilder.
StringBuffer:
StringBuilder
String c = a + b
jest równoważne String c = new StringBuilder().append(a).append(b).toString()
, więc nie jest szybsze. Tyle tylko, że tworzysz nowy dla każdego przypisania łańcucha, podczas gdy możesz mieć tylko jedno ( String d = a + b; d = d + c;
to String d = new StringBuilder().append(a).append(b).toString(); d = new StringBuilder().append(d).append(c).toString();
jednocześnie StringBuilder sb = new StringBuilder(); sb.append(a).append(b); sb.append(c); String d = sb.toString();
uratuje jedno instancje StringBuilder).
Konstruktor ciągów :
int one = 1;
String color = "red";
StringBuilder sb = new StringBuilder();
sb.append("One=").append(one).append(", Color=").append(color).append('\n');
System.out.print(sb);
// Prints "One=1, Colour=red" followed by an ASCII newline.
Bufor ciągów
StringBuffer sBuffer = new StringBuffer("test");
sBuffer.append(" String Buffer");
System.out.println(sBuffer);
Zaleca się używanie StringBuilder, gdy tylko jest to możliwe, ponieważ jest szybszy niż StringBuffer. Jeśli jednak bezpieczeństwo wątków jest konieczne, najlepszą opcją są obiekty StringBuffer.
Lepsze użycie, StringBuilder
ponieważ nie jest zsynchronizowane i dlatego zapewnia lepszą wydajność. StringBuilder
jest drop-in zamiennikiem starszych StringBuffer
.
StringBu(ff|ild)er
jest to zmienna lokalna używana tylko przez pojedynczy wątek.
Ponieważ StringBuffer
jest zsynchronizowany, wymaga dodatkowego wysiłku, dlatego opiera się na perforamance, jest nieco wolniejszy niż StringBuilder
.
Nie ma żadnych różnic między podstawowymi StringBuilder
i StringBuffer
istnieją tylko kilka różnic między nimi. W StringBuffer
metodach są zsynchronizowane. Oznacza to, że jednocześnie może działać na nich tylko jeden wątek. Jeśli jest więcej niż jeden wątek, drugi wątek będzie musiał poczekać na zakończenie pierwszego, a trzeci będzie musiał poczekać na zakończenie pierwszego i drugiego itd. Powoduje to, że proces jest bardzo wolny, a tym samym wydajność w przypadkuStringBuffer
jest niska.
Z drugiej strony StringBuilder
nie jest zsynchronizowany. Oznacza to, że wiele wątków może jednocześnie działać na tym samym StringBuilder
obiekcie. To sprawia, że proces jest bardzo szybki, a zatem wydajność StringBuilder
jest wysoka.
A String
jest niezmiennym obiektem, co oznacza, że wartość nie może być zmieniona podczas gdyStringBuffer
jest zmienna.
StringBuffer
Są synchronizowane tym samym nić bezpieczny natomiast StringBuilder
nie nadaje się i jest przeznaczona do jednorazowego gwintowany przypadkach.
Główną różnicą jest StringBuffer
synchronizacja, ale tak nie StringBuilder
jest. Jeśli musisz użyć więcej niż jednego wątku, zalecamy StringBuffer, ale jak na szybkość wykonywania StringBuilder
jest większa niż StringBuffer
, ponieważ nie jest zsynchronizowana.
Sprawdź elementy wewnętrzne zsynchronizowanej metody append StringBuffer
i niezsynchronizowanej metody append z StringBuilder
.
public StringBuffer(String str) {
super(str.length() + 16);
append(str);
}
public synchronized StringBuffer append(Object obj) {
super.append(String.valueOf(obj));
return this;
}
public synchronized StringBuffer append(String str) {
super.append(str);
return this;
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
public StringBuilder append(String str) {
super.append(str);
return this;
}
Ponieważ dodatek jest synchronized
,StringBuffer
ma narzut wydajności w porównaniu do StrinbBuilder
scenariusza wielowątkowego. Tak długo, jak nie udostępniasz bufora wielu wątkom, użyj tego StringBuilder
, co jest szybkie ze względu na brak synchronized
metod dołączania.
Oto wynik testu wydajności dla String vs StringBuffer vs StringBuilder . Wreszcie StringBuilder wygrał test. Poniżej znajduje się kod testu i wynik.
Kod :
private static void performanceTestStringVsStringbuffereVsStringBuilder() {
// String vs StringBiffer vs StringBuilder performance Test
int loop = 100000;
long start = 0;
// String
String str = null;
start = System.currentTimeMillis();
for (int i = 1; i <= loop; i++) {
str += i + "test";
}
System.out.println("String - " + (System.currentTimeMillis() - start) + " ms");
// String buffer
StringBuffer sbuffer = new StringBuffer();
start = System.currentTimeMillis();
for (int i = 1; i <= loop; i++) {
sbuffer.append(i).append("test");
}
System.out.println("String Buffer - " + (System.currentTimeMillis() - start) + " ms");
// String builder
start = System.currentTimeMillis();
StringBuilder sbuilder = new StringBuilder();
for (int i = 1; i <= loop; i++) {
sbuffer.append(i).append("test");
}
System.out.println("String Builder - " + (System.currentTimeMillis() - start) + " ms");
}
Wynik :
100000 iteracji dla dodania pojedynczego tekstu
String - 37489 ms
String Buffer - 5 ms
String Builder - 4 ms
10000 iteracji dla dodania pojedynczego tekstu
String - 389 ms
String Buffer - 1 ms
String Builder - 1 ms
StringBuffer jest zsynchronizowany i wątkowo bezpieczny, StringBuilder nie jest synchronizowany i szybszy.