Łączenie Stringów, a wydajność – dlaczego używać StringBuilder’a

Ostatnio wpadł mi w ręce kod, który robił jedną rzecz: łączył Stringi, w wyniku czego powstawał duży String. Można i tak.

Robione to było za pomocą StringBuilder’a, a więc dobrze.

Powszechną opinią jest, że trzeba używać StringBuilder’a, bo jest szybszy. O ile szybszy ? Czy zawsze?  Sprawdźmy 🙂

Weźmy najpierw na warsztat zwykłe łączenie Stringów za pomocą operatora + :

Co to to robi? Dodaje do siebie w pętli 20 tysięcy razy jakiś tekst. Ile to zajmuje czasu? U mnie w okolicach 4 sekund.  Szybko?

A teraz weźmy StringBuildera:

Czas wykonania:

1-10ms!

To nie jest szybciej. To jest cholernie szybciej! 😉

Ale czy taka różnica będzie zawsze?

Popatrzmy na dokumentację: http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.18.1
Zwłaszcza na tekst pisany małym drukiem (okazuje się, że nawet czytając dokumentację, trzeba uważać na to, co jest pisane małymi literkami 🙂 ):

An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. 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 Stringobjects that are created by evaluation of an expression.

Wynika z tego, że na etapie kompilacji kompilator może zoptymalizować kod i zamienić + na coś bardziej wydajnego. Może, ale nie musi.
W przypadku dodawania Stringów w pętli, kompilator nie potrafi dokonać optymalizacji. Stąd potrzeba użycia StringBuilder’a. Prawdopodobnie Java 9 coś w tej kwestii zmieni: http://openjdk.java.net/jeps/280