Najlepsze praktyki / wydajność: mieszanie StringBuilder.append z String.concat

86

Próbuję zrozumieć, jakie są najlepsze praktyki i dlaczego należy łączyć literały ciągów i zmienne dla różnych przypadków. Na przykład, jeśli mam taki kod

StringBuilder sb = new StringBuilder("AAAAAAAAAAAAA")
    .append(B_String).append("CCCCCCCCCCC").append(D_String)
    .append("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
    .append("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");

Czy to jest sposób na zrobienie tego? W tym poście zauważyłem, że +operator w Strings tworzy nową instancję StringBuilder, łączy operandy i zwraca konwersję typu String, która wydaje się być o wiele bardziej wymagająca niż zwykłe wywołanie .append(); więc jeśli to prawda, to nie wchodzi w rachubę. Ale co z tym String.concat()? Czy jest to właściwe .append()dla każdego konkatenacji? A może tylko dla zmiennych, a literały można dołączyć .concat()?

StringBuilder sb = new StringBuilder("AAAAAAAAAAAAA")
    .append(B_String.concat("CCCCCCCCCCC")).append(D_String
    .concat("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
    .concat("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"));

Jakie są ogólne zasady dotyczące najlepszych praktyk i wydajności w takich sytuacjach? Czy moje założenie jest słuszne +i naprawdę nie powinno być używane?

Nick Rolando
źródło
Myślę, że w zasadzie to masz. Opinie różnią się co do tego, czy String + "naprawdę nie powinno być używane". Chodzi o to, że rozumiesz konsekwencje
ControlAltDel

Odpowiedzi:

184

+ operator

String s = s1 + s2

Za kulisami jest to przetłumaczone na:

String s = new StringBuilder(s1).append(s2).toString();

Wyobraź sobie, ile dodatkowej pracy to dodaje, jeśli masz s1 + s2tutaj:

stringBuilder.append(s1 + s2)

zamiast:

stringBuilder.append(s1).append(s2)

Wiele ciągów z +

Warto zauważyć, że:

String s = s1 + s2 + s3 + ... +sN

jest tłumaczone na:

String s = new StringBuilder(s1).append(s2).append(s3)...apend(sN).toString();

concat()

String s = s1.concat(s2);

Stringtworzy char[]tablicę, która może pasować zarówno do, jak s1i s2. Kopie s1i s2zawartość do tej nowej tablicy. Właściwie wymaga mniej pracy niż +operator.

StringBuilder.append()

Utrzymuje wewnętrzną char[]tablicę, która rośnie w razie potrzeby. char[]Jeśli wewnętrzna jest wystarczająco duża, nic nie jest tworzone.

stringBuilder.append(s1.concat(s2))

jest również mało skuteczne, ponieważ s1.concat(s2)tworzy dodatkową char[]tablicę i kopie s1i s2do niego po prostu skopiować, że nowe zawartość tablicy do wewnętrznej StringBuilder char[].

Mając to na uwadze, powinieneś używać przez append()cały czas i dołączać nieprzetworzone ciągi (Twój pierwszy fragment kodu jest poprawny).

Tomasz Nurkiewicz
źródło
Dziękuję za pomoc i dogłębne wyjaśnienie; tego naprawdę potrzebowałem.
Nick Rolando,
12
Chociaż nowsze wersje kompilatora automatycznie optymalizują operator + do konstruktora ciągów, nie jest konieczne zakładanie, że tak będzie, ponieważ starsze wersje nie. Jest bardzo ważny temat, który jest ignorowany w całej tej dyskusji, który jest właściwie jednym z głównych celów StringBuildera, którym jest pula ciągów. Użycie konstruktora ciągów zachowuje wszystko jako char [] i nie tworzy pośredniego ciągu znaków, takiego jak operator + przed optymalizacją kompilatora (używany do wykonywania po prostu konkatowania). Użycie concat tworzy pośredni obiekt String, który jest buforowany, ale nie jest używany.
LINEMAN78
Więc jeśli wykonuję jednorazową instrukcję (a nie wiele instrukcji w ramach metody), konkatenację na kilku różnych ciągach. Byłoby dobrze użyć +na obiektach String zamiast tworzyć StringBuilderdla tej jednorazowej konkatenacji, ponieważ zasadniczo zrobi to samo?
Nick Rolando,
1
A jeśli chcę konkatować tylko znak, czy lepiej jest użyć .append ('\ n') zamiast .append ("\ n")? Ponieważ znak jest typem pierwotnym, a ciąg znaków jest obiektem?
vrwim
3
Efektywna druga edycja Java : wielokrotne używanie operatora konkatenacji ciągów w celu konkatenacji n ciągów wymaga czasu kwadratowego w n. Czy jest to nadal aktualne?
gkiko
16

Kompilator optymalizuje + konkatenację.

Więc

int a = 1;
String s = "Hello " + a;

jest przekształcany w

new StringBuilder().append("Hello ").append(1).toString();

Jest tutaj doskonały temat wyjaśniający, dlaczego powinieneś używać operatora +.

Andy Turner
źródło
3
Kompilator faktycznie by to zoptymalizował, String s = "Hello World";ponieważ używasz literałów.
ColinD,
@ColinD: +1. Właśnie zmodyfikowałem mój fragment.
3

Optymalizacja jest wykonywana automatycznie przez kompilator.

Kompilator Java2 automatycznie skonwertuje następujące elementy:

String s = s1 + s2; 

do

String s = (new StringBuffer()).append(s1).append(s2).toString();

Zaczerpnięte prosto z witryny Java Best Practices on Oracles.

BoldAsLove
źródło
2

Powinieneś zawsze używać append.

concatstwórz nowy ciąg, żeby wyglądał całkiem tak +, jak myślę.

Jeśli concatużywasz +z 2 końcowymi ciągami znaków, JVM może dokonać optymalizacji, więc jest to to samo, co dodanie w tym przypadku.

alain.janinm
źródło
1

Jeśli połączysz dokładnie dwa Strings, użyj String.concat (tworzy nowy String, tworząc nową tablicę znaków, która pasuje do obu Strings i kopiuje do niej tablice char obu Strings).

Jeśli połączysz wiele (więcej niż dwa) ciągów w jednym wierszu, użyj + lub StringBuilder.append, nie ma to znaczenia, ponieważ kompilator konwertuje + na StringBuilder.append. Jest to dobre dla wielu ciągów, ponieważ utrzymuje jedną tablicę znaków, która rośnie w razie potrzeby.

Jeśli łączysz wiele ciągów w wielu wierszach, utwórz jeden StringBuilder i użyj metody append. Na koniec, gdy skończysz dołączać ciągi do obiektu StringBuilder, użyj jego metody .toString (), aby utworzyć z niego String. W przypadku łączenia w wielu wierszach jest to szybsze niż druga metoda, ponieważ druga metoda utworzyłaby nowy StringBuilder w każdym wierszu, dołączyłby ciągi, a następnie rzutowałby z powrotem na String, podczas gdy trzecia metoda używa tylko jednego StringBuildera do całości.

Dakkaron
źródło
0

Użyj +operatora jest najlepszą praktyką, jest również prosty i czytelny.

Język Java zapewnia specjalne wsparcie dla operatora łączenia ciągów (+) oraz dla konwersji innych obiektów na łańcuchy. Konkatenacja ciągów znaków jest implementowana za pomocą klasy StringBuilder (lub StringBuffer) i jej metody dołączania.

Oficjalny dokument: https://docs.oracle.com/javase/8/docs/api/java/lang/String.html

Do Nhu Vy
źródło
0

na poziomie kodu bajtowego nie ma różnicy i nie narażamy tam wydajności. W przypadku wykonywania kodu bajtowego musi przejść przez metodę przeciążania operatora nieliniowego dla +, wywołując append. następnie na poziomie języka asemblera (Java jest napisana w C, a C tworzy zestawy podobne do asemblera, będzie dodatkowe wywołanie rejestru do przechowywania + wywołanie metody na stosie i dodatkowe wypychanie. (w rzeczywistości kompilator krzyżowy może optymalizować + operator zadzwonić, w takim przypadku nie ma to znaczenia dla skuteczności).

Dobrą praktyką jest posiadanie jednego sposobu na zwiększenie czytelności. :)

Shevon Silva
źródło
0

Osobiście wolę Strings.format()prosty, czytelny, jednowierszowy program do formatowania ciągów .

String b = "B value";
String d = "D value";
String fullString = String.format("A %s C %s E F", b, d);
// Output: A B value C D value E F
T04435
źródło
0

Wszystkie odpowiedzi są całkiem dobre i wyjaśniające. Ale czułem, że badanie innych technik łączenia strun również może pomóc, takich jak - Guava Joiner, Streams, String.format itp.

Aby uzyskać szczegółowe informacje na temat wydajności każdej techniki konkatenacji, java-string-concatenation-which-way-is-best .

W skrócie, wydajność konkatenacji zmienia się bez. ciągów do konkatenacji. Na przykład - aby połączyć 1-10 ciągów, te techniki działają najlepiej - StringBuilder, StringBuffer i Operator Plus. Aby połączyć setki strun - Guava Joiner, biblioteka apache's stringsUtils również działa świetnie.

Przejdź przez powyższy blog. To naprawdę bardzo dobrze wyjaśnia wydajność wydajności.

Dzięki.

coderAJ
źródło