Biorąc pod uwagę 2 toString()
poniższe implementacje, które są preferowane:
public String toString(){
return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}
lub
public String toString(){
StringBuilder sb = new StringBuilder(100);
return sb.append("{a:").append(a)
.append(", b:").append(b)
.append(", c:").append(c)
.append("}")
.toString();
}
?
Co ważniejsze, biorąc pod uwagę, że mamy tylko 3 właściwości, może to nie mieć znaczenia, ale w którym momencie +
przeszedłbyś z concat na StringBuilder
?
java
performance
string
concatenation
stringbuilder
niesekwencyjny
źródło
źródło
StringBuilder
jaki rozmiar przydzielić z góry, w przeciwnym razie, jeśli zabraknie miejsca, będzie musiał podwoić rozmiar, tworząc nowachar[]
tablica następnie kopiuje dane - co jest kosztowne. Możesz oszukiwać, podając rozmiar, a wtedy nie ma potrzeby tworzenia tablicy - więc jeśli uważasz, że Twój łańcuch będzie miał ~ 100 znaków, możesz ustawić StringBuilder na ten rozmiar i nigdy nie będzie on musiał się rozszerzać wewnętrznie.Odpowiedzi:
Wersja 1 jest preferowana, ponieważ jest krótsza, a kompilator faktycznie zmieni ją w wersję 2 - bez różnicy wydajności.
W miejscu łączenia w pętli - zwykle wtedy kompilator nie może
StringBuilder
sam się zastąpić .źródło
To increase the performance of repeated string concatenation, a Java compiler _may_ use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
kluczowe słowo może istnieć . Biorąc pod uwagę, że jest to oficjalnie opcjonalne (choć najprawdopodobniej wdrożone), czy nie powinniśmy się chronić?Kluczem jest to, czy piszesz pojedynczą konkatenację w jednym miejscu, czy gromadzisz ją z czasem.
W podanym przykładzie nie ma sensu jawnie używać StringBuilder. (Spójrz na skompilowany kod pierwszego przypadku.)
Ale jeśli budujesz ciąg np. Wewnątrz pętli, użyj StringBuilder.
Aby to wyjaśnić, zakładając, że hugeArray zawiera tysiące ciągów, koduje się tak:
jest bardzo marnujący czas i pamięć w porównaniu z:
źródło
result += s;
(w pierwszym przykładzie)"{a:"+ a + ", b:" + b + ", c: " + c +"}";
Wolę:
... ponieważ jest krótki i czytelny.
Chciałbym nie zoptymalizować pod kątem szybkości, chyba go używać wewnątrz pętli z bardzo dużą liczbą powtórzeń i zmierzyli różnicę wydajności.
Zgadzam się, że jeśli musisz podać wiele parametrów, ten formularz może być mylący (jak mówi jeden z komentarzy). W tym przypadku przerzuciłbym się na bardziej czytelną formę (być może używając ToStringBuilder z apache-commons - wzięte z odpowiedzi Matt b) i ponownie zignorowałem wydajność.
źródło
null
W większości przypadków nie widać rzeczywistej różnicy między tymi dwoma podejściami, ale łatwo jest stworzyć najgorszy scenariusz, taki jak ten:
Dane wyjściowe to:
Problem polega na tym, że dodanie + = do łańcucha rekonstruuje nowy łańcuch, więc kosztuje coś liniowego do długości łańcuchów (suma obu).
Więc - na twoje pytanie:
Drugie podejście byłoby szybsze, ale jest mniej czytelne i trudniejsze w utrzymaniu. Jak powiedziałem, w twoim konkretnym przypadku prawdopodobnie nie zobaczysz różnicy.
źródło
+=
, oryginalny przykład był sekwencją+
, którą kompilator przekształca w pojedynczestring.concat
wywołanie. Twoje wyniki nie mają zastosowania.Miałem też spór z szefem o to, czy użyć append, czy +. Ponieważ używają Append (wciąż nie mogę się domyślić, jak mówią za każdym razem, gdy tworzony jest nowy obiekt). Pomyślałem więc, żeby zrobić kilka prac badawczo-rozwojowych. Chociaż uwielbiam wyjaśnienia Michaela Borgwardta, ale chciałem tylko wyjaśnić, czy ktoś naprawdę będzie musiał wiedzieć w przyszłości.
i demontaż wyższej klasy wychodzi jako
Z powyższych dwóch kodów widać, że Michael ma rację. W każdym przypadku tworzony jest tylko jeden obiekt SB.
źródło
Od wersji Java 1.5 proste łączenie jednowierszowe za pomocą „+” i StringBuilder.append () generuje dokładnie ten sam kod bajtowy.
Dlatego ze względu na czytelność kodu użyj „+”.
2 wyjątki:
źródło
W najnowszej wersji Javy (1.8) dezasemblacja (
javap -c
) pokazuje optymalizację wprowadzoną przez kompilator.+
równieżsb.append()
wygeneruje bardzo podobny kod. Warto jednak sprawdzić zachowanie, jeśli korzystamy+
z pętli for.Dodawanie ciągów za pomocą + w pętli for
Jawa:
ByteCode :(
for
fragment pętli)Dodawanie ciągów za pomocą stringbuilder.append
Jawa:
ByteCdoe :(
for
fragment pętli)Jest jednak rażąca różnica . W pierwszym przypadku, gdy
+
został użyty,StringBuilder
dla każdej iteracji pętli tworzony jest nowy, a wygenerowany wynik jest zapisywany przez wykonanietoString()
wywołania (29–41). Generujesz więc łańcuchy pośrednie, których tak naprawdę nie potrzebujesz podczas używania+
operatora wfor
pętli.źródło
W Javie 9 wersja 1 powinna być szybsza, ponieważ jest konwertowana na
invokedynamic
wywołanie. Więcej informacji można znaleźć w JEP-280 :źródło
Ze względu na wydajność odradza się stosowanie
+=
(String
konkatenacji). Powód jest następujący: JavaString
jest niezmienna, za każdym razem, gdy wykonywana jest nowa konkatenacja,String
tworzony jest nowy (nowy ma inny odcisk palca od starszego już w puli ciągów ). Tworzenie nowych ciągów naciska na GC i spowalnia program: tworzenie obiektów jest kosztowne.Poniższy kod powinien uczynić go bardziej praktycznym i czytelnym jednocześnie.
Wyniki dla biegu podano poniżej.
Nie biorąc pod uwagę wyników dla 1 konkatenacji (JIT jeszcze nie wykonał swojej pracy), nawet dla 10 konkatenacji kara za wyniki jest istotna; dla tysięcy konkatenacji różnica jest ogromna.
Wnioski wyciągnięte z tego bardzo szybkiego eksperymentu (łatwego do odtworzenia z powyższym kodem): nigdy nie używaj
+=
do konkatenacji ciągów razem, nawet w bardzo podstawowych przypadkach, gdy potrzebnych jest kilka konkatenacji (jak powiedziano, tworzenie nowych ciągów jest i tak drogie i wywiera presję na GC).źródło
Zobacz przykład poniżej:
Wynik:
Wraz ze wzrostem długości łańcucha rośnie czas konkatenacji.
To jest
StringBuilder
zdecydowanie potrzebne.Jak widać, konkatenacja:
UUID.randomUUID()+"---"
tak naprawdę nie wpływa na czas.PS: Nie sądzę, kiedy korzystanie z StringBuilder w Javie jest naprawdę duplikatem tego.
To pytanie mówi o tym,
toString()
które przez większość czasu nie wykonują konkatenacji wielkich łańcuchów.Aktualizacja 2019
Od
java8
czasów wszystko się nieco zmieniło. Wydaje się, że teraz (java13) czas konkatenacji+=
jest praktycznie taki sam jakstr.concat()
. JednakStringBuilder
czas konkatenacji jest wciąż stały . (Oryginalny post powyżej został nieco zmodyfikowany, aby dodać więcej pełnych wyników)Warto zauważyć, że
bytecode:8/java.version:13
kombinacja ma dobrą wydajność w porównaniu dobytecode:8/java.version:8
źródło
java13
których teraz są inne. Ale myślę, że główny wniosek pozostaje ten sam.Apache Commons-Lang ma klasę ToStringBuilder, która jest bardzo łatwa w użyciu. Wykonuje niezłą robotę zarówno w logice append, jak i formatowaniu tego, jak chcesz, aby twój toString wyglądał.
Zwraca wynik, który wygląda
com.blah.YourClass@abc1321f[a=whatever, b=foo]
.Lub w bardziej skondensowanej formie za pomocą łączenia:
Lub jeśli chcesz użyć refleksji, aby uwzględnić wszystkie pola klasy:
Możesz także dostosować styl ToString, jeśli chcesz.
źródło
Spraw, aby metoda toString była jak najbardziej czytelna!
Jedynym wyjątkiem w mojej książce jest to, że możesz mi udowodnić , że pochłania ona znaczne zasoby :) (Tak, to znaczy profilowanie)
Zauważ też, że kompilator Java 5 generuje szybszy kod niż odręczne podejście „StringBuffer” używane we wcześniejszych wersjach Java. Jeśli użyjesz „+”, to i przyszłe rozszerzenia będą dostępne za darmo.
źródło
Wydaje się, że istnieje debata, czy użycie StringBuilder jest nadal potrzebne w obecnych kompilatorach. Pomyślałem więc, że dam 2 centy doświadczenia.
Mam
JDBC
zestaw wyników 10 000 rekordów (tak, potrzebuję ich wszystkich w jednej partii). Korzystanie z operatora + zajmuje około 5 minut na moim komputerzeJava 1.8
. UżyciestringBuilder.append("")
zajmuje mniej niż sekundę dla tego samego zapytania.Różnica jest więc ogromna. Wewnątrz pętli
StringBuilder
jest znacznie szybszy.źródło
Pod względem wydajności Łączenie ciągów za pomocą „+” jest droższe, ponieważ musi utworzyć zupełnie nową kopię ciągu, ponieważ ciągi są niezmienne w Javie. Odgrywa to szczególną rolę, jeśli konkatenacja jest bardzo częsta, np .: wewnątrz pętli. Oto, co sugeruje moja IDEA, gdy próbuję zrobić coś takiego:
Główne zasady:
Oto miły blog Jona Skeeta na ten temat.
źródło
Czy mogę wskazać, że jeśli zamierzasz iterować kolekcję i używać StringBuilder, możesz wypróbować Apache Commons Lang i StringUtils.join () (w różnych smakach)?
Niezależnie od wydajności, zaoszczędzi Ci konieczności tworzenia StringBuilders i pętli, co wydaje się być milionowym czasem.
źródło
Oto, co sprawdziłem w Javie8
Korzystanie z StringBuilder
źródło
Myślę, że powinniśmy zastosować podejście dołączania StringBuilder. Powodem jest:
Konkatenat String utworzy za każdym razem nowy obiekt string (ponieważ String jest obiektem niezmiennym), więc utworzy 3 obiekty.
W Konstruktorze ciągów utworzony zostanie tylko jeden obiekt [StringBuilder jest mutowalny], a dalszy ciąg zostanie do niego dołączony.
źródło
Do takich prostych łańcuchów wolę używać
Chciałbym powiedzieć, że preferowaną metodą konstruowania łańcucha jest użycie StringBuilder, String # concat (), a następnie przeciążonego operatora +. StringBuilder to znaczny wzrost wydajności, gdy praca z dużymi łańcuchami, podobnie jak użycie operatora +, oznacza duży spadek wydajności (wykładniczo duży spadek wraz ze wzrostem wielkości łańcucha). Jedynym problemem związanym z używaniem .concat () jest to, że może on zgłaszać wyjątki NullPointerExceptions.
źródło