2017-09-19 12 views
6

W JDK 8, StringBuffer klasa ma toStringCache, natomiast StringBuilder nie.Dlaczego StringBuffer ma toStringCache, a StringBuilder nie?

/** 
* A cache of the last value returned by toString. Cleared 
* whenever the StringBuffer is modified. 
*/ 
private transient char[] toStringCache; 

Ale dlaczego?

  • Możliwym powodem mogę myśleć, że StringBuffer jest już zsynchronizowane tak cache mogą być realizowane łatwiejsze.

  • A może historycznie StringBuffer został zaimplementowany w ten sposób, więc stary kod w dużej mierze zależy od tej funkcji?

Biorąc pod uwagę współczesną maszynę JVM z analizą ucieczki i tendencyjnym blokowaniem, czy różnica jest już istotna?

+0

Twoje pierwsze przypuszczenie jest poprawne. – Flown

+0

@Flown Czy to się opłaca? Czy wystąpi jakikolwiek wzrost wydajności? Więcej kodu oznacza więcej błędów. – ntysdd

+1

Jeśli wielokrotnie będziesz wywoływał 'StringBuffer :: toString', wtedy będzie korzyść. – Flown

Odpowiedz

3

Myślę, że pierwsze przypuszczenie jest bardzo dokładna, ponieważ StringBuilder nie jest bezpieczny wątku i instancją mogą być współużytkowane przez wiele wątków, wykonawczych taką pamięć będzie wymagać dodatkowej synchronizacji, byłoby to pokonać cel StringBuilder w pierwszym miejsce.

Co do tego, dlaczego byłoby to potrzebne, sprowadza się do użytego konstruktora new String(...); w przypadku StringBuffer że używane konstruktora String(array, boolean) komentarz mówi:

Package prywatny konstruktor, który dzieli tablicę wartości dla prędkości.

+1

Być może warto zauważyć, że przewaga prędkości nie występuje, jeśli wywołasz 'StringBuffer.toString' za jednym razem, jako [wywołujący] (http://grepcode.com/file/repository.grepcode.com/java/root/ jdk/openjdk/8u40-b25/java/lang/StringBuffer.java # 669) wykonał wtedy kopię. Może być niewielka korzyść, tylko w rzadkich przypadkach wielokrotnych wywołań bez zmian. Ten konstruktor 'String' zapewnia więcej korzyści dla fabryk, które bezpośrednio pracują nad poprawionym rozmiarem bufora' char [] 'bez późniejszego ponownego użycia, powiedz' String.concat (String) ',' String.replace (char, char) ',' Integer .toString (int) ', itp. – Holger

4

Może pomóc w rozważeniu historycznego kontekstu. StringBuilder został wprowadzony w Javie 5, ponieważ uznano, że StringBuffer nie jest dobrze dopasowany do rzeczywistych przypadków użycia.

Nowo wprowadzony model został zaprojektowany z myślą o głównym zastosowaniu, ponieważ został skonstruowany, użyty i natychmiast porzucony w czysto lokalnym kontekście. Dlatego nie zapewnia żadnej synchronizacji i nie zadaje sobie trudu zoptymalizowania rzadkiego przypadku, w którym jego metoda toString() jest wywoływana wiele razy bez pośredniej zmiany (kiedy dzieje się to w rzeczywistości?), Zwłaszcza, że ​​rzeczywiście , zapewnienie funkcji buforowania bez utraty przewagi wydajności bez synchronizacji wątku, jest gdzieś pomiędzy "twardym" do "niemożliwym".

Podczas StringBuilder dokumentuje się nie wątku bezpieczne, więc wiesz niespójne rzeczy mogą się zdarzyć podczas wywoływania metody na nim jednocześnie, klasa String jest gwarancją pewności poprzez niezmienności wątek, a więc to nie może być dozwolone, że StringBuilder " s brak synchronizacji może powodować niespójności w już skonstruowanych łańcuchach, a niezaplanowanie tablicy między wartościami String i StringBuilder jest najbezpieczniejszym rozwiązaniem.

Dlaczego więc ta optymalizacja istnieje, skoro prawie nigdy nie ma korzyści w prawdziwym życiu? No cóż, ponieważ jest tam od bardzo dawna, najprawdopodobniej nawet od wersji 1.0 i nie warto nic zmieniać w klasie StringBuffer. Jego obecność może nie mieć żadnej realnej przewagi, ale też nie ma jej usunięcia, co wymagałoby nowych testów itd. I może okazać się, że jest to space bar overheating feature dla niektórych aplikacji ...

Możesz zauważyć, że w tamtym czasie, w Javie 1 .x, podjęto wiele decyzji projektowych, które dziś mogą wydawać się dziwne.Nadużywanie klas synchronized w klasach podstawowych jest jednym z nich, a to rzadko pomaga w optymalizacji innego. W tym czasie nawet implikacje niezmienności nie zostały dobrze zrozumiane, dlatego mamy nadmiarowe metody, takie jak String.valueOf(char[]) i String.copyValueOf(char[]), plus możliwość użycia new String(char[]) ...

+0

Nie znałem String.copyValueOf (char []) – ntysdd

+1

@ntysdd: ... i istnienie tych nadmiarowych metod' copyValueOf' jest czymś, czego nie musimy pamiętać. Przynajmniej dla codziennej pracy programisty. – Holger

+2

@Dysysdd (i @Holger) Oczywiście metody 'copyValueOf' są dziś nieistotne. Uwaga historyczna jest taka, że ​​pre JDK 1.0, konstruktor 'String (char [])' i 'valueOf (char [])' (oba publiczne) po prostu zapisują odwołanie do argumentu w nowo skonstruowanym łańcuchu. Naturalnie doprowadziło to do zmiany łańcuchów! Metody 'copyValueOf' zostały dodane w celu skopiowania tablic, następnie konstruktory i' valueOf' zostały zmodyfikowane, aby tworzyć kopie, pozostawiając metody 'copyValueOf' nadmiarowe. Jednak nigdy nie zostały oczyszczone. –