String concatenation: Concat() vs operator "+"
Zakładając ciąg a i b:
a += b
a = a.concat(b)
Pod maską, czy to to samo?
Tutaj jest concat dekompilowany jako odniesienie. Chciałbym być w stanie dekompilować operator +
, aby zobaczyć, co to robi.
public String concat(String s) {
int i = s.length();
if (i == 0) {
return this;
}
else {
char ac[] = new char[count + i];
getChars(0, count, ac, 0);
s.getChars(0, i, ac, count);
return new String(0, count + i, ac);
}
}
11 answers
Nie, niezupełnie.
Po pierwsze, jest niewielka różnica w semantyce. Jeśli a
jest null
, to a.concat(b)
rzuca NullPointerException
, ale a+=b
potraktuje oryginalną wartość a
tak, jakby była null
. Co więcej, metoda concat()
akceptuje tylko wartości String
, podczas gdy operator +
po cichu przekonwertuje argument na łańcuch znaków (używając metody toString()
dla obiektów). Więc metoda concat()
jest bardziej rygorystyczna w tym, co akceptuje.
Aby zajrzeć pod maskę, napisz prostą klasę z a += b;
public class Concat {
String cat(String a, String b) {
a += b;
return a;
}
}
Teraz demontować z javap -c
(zawarte w Sun JDK). Powinieneś zobaczyć listę zawierającą:
java.lang.String cat(java.lang.String, java.lang.String);
Code:
0: new #2; //class java/lang/StringBuilder
3: dup
4: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V
7: aload_1
8: invokevirtual #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: aload_2
12: invokevirtual #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: invokevirtual #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/ String;
18: astore_1
19: aload_1
20: areturn
Więc a += b
jest odpowiednikiem
a = new StringBuilder()
.append(a)
.append(b)
.toString();
Metoda concat
powinna być szybsza. Jednak przy większej liczbie ciągów metoda StringBuilder
wygrywa, przynajmniej pod względem wydajności.
Kod źródłowy String
i StringBuilder
(oraz jego Klasa package-private base) jest dostępny w src.zip Of The Sun JDK. Możesz zobaczyć, że tworzysz tablicę znaków (zmiana rozmiaru w razie potrzeby), a następnie wyrzucenie go podczas tworzenia ostatecznego String
. W praktyce alokacja pamięci jest zaskakująco szybka.
Aktualizacja: jak zauważa Paweł Adamski, wydajność zmieniła się w nowszym hotspocie. javac
nadal produkuje dokładnie ten sam kod, ale kompilator kodu bajtowego oszukuje. Proste testowanie całkowicie zawodzi, ponieważ cały kod jest wyrzucany. Sumowanie System.identityHashCode
(Nie String.hashCode
) pokazuje, że kod StringBuffer
ma niewielką przewagę. Może ulec zmianie, gdy następny aktualizacja zostanie wydana lub jeśli używasz innego JVM. From @lukaseder, lista hotspot JVM intrinsics .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-12 12:57:00
Niyaz jest poprawny, ale warto również zauważyć, że operator special + może być przekształcony w coś bardziej wydajnego przez kompilator Javy. Java posiada klasę StringBuilder, która reprezentuje bezpieczny dla wątków, zmienny Łańcuch znaków. Po wykonaniu kilku konkatenacji, kompilator Javy po cichu konwertuje
String a = b + c + d;
Do
String a = new StringBuilder(b).append(c).append(d).toString();
Który dla dużych strun jest znacznie bardziej wydajny. Z tego co wiem, to nie dzieje się tak, gdy używasz concat metoda.
Jednak metoda concat jest bardziej efektywna podczas łączenia pustego łańcucha z istniejącym łańcuchem. W takim przypadku JVM nie musi tworzyć nowego obiektu String i może po prostu zwrócić istniejący. Zobacz dokumentację concat , Aby to potwierdzić.
Więc jeśli jesteś bardzo zaniepokojony wydajnością, powinieneś użyć metody concat podczas łączenia możliwie-pustych łańcuchów i użyć + w przeciwnym razie. Jednak różnica w wydajności powinna być i pewnie nie powinieneś się o to martwić.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:31
Przeprowadziłem podobny test jak @marcio, ale z następującą pętlą:
String c = a;
for (long i = 0; i < 100000L; i++) {
c = c.concat(b); // make sure javac cannot skip the loop
// using c += b for the alternative
}
Na dobrą sprawę, ja też dorzuciłem StringBuilder.append()
. Każdy test był prowadzony 10 razy, z 100k powtórzeń dla każdego biegu. Oto wyniki:
-
StringBuilder
wygrywa. Wynik zegara wynosił 0 dla większości biegów, a najdłuższy trwał 16ms. -
a += b
zajmuje około 40000ms (40s) na każdy bieg. -
concat
wymaga tylko 10000ms (10s) na run.
Nie dekompilowałem Klasa, aby zobaczyć wewnętrzne lub uruchomić go przez profiler, ale podejrzewam, że a += b
spędza dużo czasu na tworzeniu nowych obiektów StringBuilder
, a następnie konwertowaniu ich z powrotem do String
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-06 19:25:12
Tom ma rację opisując dokładnie to, co robi operator+. Tworzy tymczasowe StringBuilder
, dołącza części i kończy się toString()
.
Jednak wszystkie odpowiedzi do tej pory ignorują efekty optymalizacji czasu pracy hotspot. W szczególności te tymczasowe operacje są rozpoznawane jako wspólny wzorzec i zastępowane bardziej wydajnym kodem maszynowym w czasie wykonywania.
@marcio: stworzyłeś mikro-benchmark ; z nowoczesnymi JVM to nie jest prawidłowy sposób na kod profilu.
Powodem, dla którego optymalizacja czasu pracy ma znaczenie, jest to, że wiele z tych różnic w kodzie-nawet w tym tworzenie obiektów-jest zupełnie inne, gdy HotSpot zacznie działać. Jedynym sposobem na upewnienie się jest profilowanie kodu in situ.
Wreszcie, wszystkie te metody są niesamowicie szybkie. Może to być przypadek przedwczesnej optymalizacji. Jeśli masz kod, który dużo łączy ciągi, sposób na uzyskanie maksymalnej prędkości prawdopodobnie nie ma nic wspólnego z jakimi operatorami wybierasz, a zamiast tego algorytm, którego używasz!Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-06 19:19:52
Co powiesz na proste testy? Użyto poniższego kodu:
long start = System.currentTimeMillis();
String a = "a";
String b = "b";
for (int i = 0; i < 10000000; i++) { //ten million times
String c = a.concat(b);
}
long end = System.currentTimeMillis();
System.out.println(end - start);
- Wersja
"a + b"
wykonana w 2500ms. -
a.concat(b)
wykonane w 1200ms.
concat()
zajęło średnio połowę czasu.
Ten wynik zaskoczył mnie, ponieważ metoda concat()
zawsze tworzy nowy łańcuch znaków (zwraca "new String(result)
". Wiadomo, że:
String a = new String("a") // more than 20 times slower than String a = "a"
Dlaczego kompilator nie był w stanie zoptymalizować ciągu tworzenie w kodzie "a + b", znając zawsze ten sam ciąg znaków? To może uniknąć tworzenia nowego ciągu. Jeśli nie wierzysz w powyższe stwierdzenie, przetestuj dla siebie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-10 20:55:32
Większość odpowiedzi pochodzi z 2008 roku. Wygląda na to, że rzeczy zmieniły się na przestrzeni czasu. Moje najnowsze benchmarki wykonane za pomocą JMH pokazują, że w Javie 8 +
jest około dwa razy szybszy niż concat
.
Mój benchmark:
@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class StringConcatenation {
@org.openjdk.jmh.annotations.State(Scope.Thread)
public static class State2 {
public String a = "abc";
public String b = "xyz";
}
@org.openjdk.jmh.annotations.State(Scope.Thread)
public static class State3 {
public String a = "abc";
public String b = "xyz";
public String c = "123";
}
@org.openjdk.jmh.annotations.State(Scope.Thread)
public static class State4 {
public String a = "abc";
public String b = "xyz";
public String c = "123";
public String d = "!@#";
}
@Benchmark
public void plus_2(State2 state, Blackhole blackhole) {
blackhole.consume(state.a+state.b);
}
@Benchmark
public void plus_3(State3 state, Blackhole blackhole) {
blackhole.consume(state.a+state.b+state.c);
}
@Benchmark
public void plus_4(State4 state, Blackhole blackhole) {
blackhole.consume(state.a+state.b+state.c+state.d);
}
@Benchmark
public void stringbuilder_2(State2 state, Blackhole blackhole) {
blackhole.consume(new StringBuilder().append(state.a).append(state.b).toString());
}
@Benchmark
public void stringbuilder_3(State3 state, Blackhole blackhole) {
blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).toString());
}
@Benchmark
public void stringbuilder_4(State4 state, Blackhole blackhole) {
blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).append(state.d).toString());
}
@Benchmark
public void concat_2(State2 state, Blackhole blackhole) {
blackhole.consume(state.a.concat(state.b));
}
@Benchmark
public void concat_3(State3 state, Blackhole blackhole) {
blackhole.consume(state.a.concat(state.b.concat(state.c)));
}
@Benchmark
public void concat_4(State4 state, Blackhole blackhole) {
blackhole.consume(state.a.concat(state.b.concat(state.c.concat(state.d))));
}
}
Wyniki:
Benchmark Mode Cnt Score Error Units
StringConcatenation.concat_2 thrpt 50 24908871.258 ± 1011269.986 ops/s
StringConcatenation.concat_3 thrpt 50 14228193.918 ± 466892.616 ops/s
StringConcatenation.concat_4 thrpt 50 9845069.776 ± 350532.591 ops/s
StringConcatenation.plus_2 thrpt 50 38999662.292 ± 8107397.316 ops/s
StringConcatenation.plus_3 thrpt 50 34985722.222 ± 5442660.250 ops/s
StringConcatenation.plus_4 thrpt 50 31910376.337 ± 2861001.162 ops/s
StringConcatenation.stringbuilder_2 thrpt 50 40472888.230 ± 9011210.632 ops/s
StringConcatenation.stringbuilder_3 thrpt 50 33902151.616 ± 5449026.680 ops/s
StringConcatenation.stringbuilder_4 thrpt 50 29220479.267 ± 3435315.681 ops/s
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-29 08:56:14
Zasadniczo istnieją dwie istotne różnice między metodą + a metodą concat
.
-
Jeśli używasz metody concat, wtedy będziesz mógł łączyć łańcuchy tylko wtedy, gdy w przypadku + operator, można również połączyć łańcuch z dowolnym typem danych.
Na Przykład:
String s = 10 + "Hello";
W tym przypadku wyjście powinno być 10Hello.
String s = "I"; String s1 = s.concat("am").concat("good").concat("boy"); System.out.println(s1);
W powyższym przypadku musisz podać dwa ciągi znaków obowiązkowe.
-
Druga i główna różnica między + i concat to:
Przypadek 1: Załóżmy, że łączę te same Ciągi z operatorem concat w ten sposób
String s="I"; String s1=s.concat("am").concat("good").concat("boy"); System.out.println(s1);
W tym przypadku całkowita liczba obiektów utworzonych w Puli wynosi 7 w ten sposób:
I am good boy Iam Iamgood Iamgoodboy
Case 2:
Teraz będę konkatinował te same struny poprzez + operator
String s="I"+"am"+"good"+"boy"; System.out.println(s);
W powyższym przypadku całkowita liczba obiektów utworzonych jest tylko 5.
Faktycznie, kiedy konkatinujemy struny poprzez + operator następnie utrzymuje klasę StringBuffer do wykonania tego samego zadania w następujący sposób: -
StringBuffer sb = new StringBuffer("I"); sb.append("am"); sb.append("good"); sb.append("boy"); System.out.println(sb);
W ten sposób stworzy tylko pięć obiektów.
Więc chłopaki to są podstawowe różnice między + oraz metodą concat . Enjoy :)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-05 21:15:53
Dla kompletności chciałem dodać, że definicję operatora " + " można znaleźć w JLS SE8 15.18.1:
Jeżeli tylko jedno wyrażenie operandowe jest typu String, to string konwersja (§5.1.11) jest wykonywana na drugim operandie w celu wytworzenia / align = "left" /
Wynik konkatenacji łańcuchowej jest odniesieniem do obiektu Łańcuchowego to jest konkatenacja dwóch ciągów operandowych. Postacie z lewej strony operand poprzedzają znaki prawej ręki operand w nowo utworzonym smyczku.
Obiekt String jest nowo utworzony (§12.5), chyba że wyrażenie jest wyrażenie stałe (§15.28).
O implementacji JLS mówi co następuje:
implementacja może wybrać konwersję i konkatenację w jednym kroku, aby uniknąć tworzenia, a następnie odrzucenia pośredniego Obiekt typu String. Aby zwiększyć wydajność powtarzanego ciągu concatenation, kompilator Javy może używać klasy StringBuffer lub podobna technika zmniejszania liczby pośrednich obiektów łańcuchowych które powstają w wyniku oceny wyrażenia.
W przypadku typów prymitywnych implementacja może również zoptymalizować tworzenie obiektu wrappera poprzez konwersję bezpośrednio z prymitywnego wpisz Do ciągu znaków.
Więc sądząc po ' kompilator Javy może używać klasy StringBuffer lub podobnej techniki do reduce', różne Kompilatory mogą produkować inny kod bajtowy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-26 15:00:44
Operator + może pracować między łańcuchem znaków a wartością typu danych string, char, integer, double lub float. Po prostu konwertuje wartość na jej reprezentację łańcuchową przed konkatenacją.
Operator concat może być wykonywany tylko na i z łańcuchami. Sprawdza zgodność typu danych i wyświetla błąd, jeśli nie pasują.
Poza tym, podany kod robi to samo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-01 18:03:27
Nie sądzę.
a.concat(b)
jest zaimplementowany w String i myślę, że implementacja niewiele się zmieniła od wczesnych maszyn java. Implementacja operacji +
zależy od wersji Javy i kompilatora. Obecnie +
jest zaimplementowany przy użyciu StringBuffer
aby operacja była jak najszybsza. Może w przyszłości to się zmieni. We wcześniejszych wersjach Javy +
działanie na łańcuchach znaków było znacznie wolniejsze, ponieważ dawało wyniki pośrednie.
Myślę, że +=
jest zaimplementowane przy użyciu +
i podobnie zoptymalizowane.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-27 14:46:12
Przy użyciu+, prędkość maleje wraz ze wzrostem długości łańcucha, ale przy użyciu concat, prędkość jest bardziej stabilna, a najlepszą opcją jest użycie klasy StringBuilder, która ma stabilną prędkość w tym celu.
Chyba rozumiesz dlaczego. Ale całkowicie najlepszym sposobem tworzenia długich łańcuchów jest użycie StringBuilder () i append (), obie prędkości będą niedopuszczalne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-05 21:18:03