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?


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).


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 +.


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.


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.


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.


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


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. :)


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

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.