StringBuilder i StringBuffer w Javie
Przetwarzanie danych tekstowych należy do jednych z ważniejszych elementów pracy przy komputerze. Na początku naszego szkolenia już wspominaliśmy o klasie String
. Nadszedł czas na nieco szersze ujęcie problemu łańcuchów znakowych – bardziej od strony technicznej.
Budowa klasy String
Wśród typów prostych tylko char służy do przechowywania znaków. Jednakże dopiero tablica pozwala umieszczać całe ciągi znakowe. W Javie klasa String
używa tablicę byte.
Pola
Klasa String
składa się z wielu pól, ale nas interesuje najważniejsze.
1 |
private final byte[] value; |
Zwróćmy uwagę na to, że to pole jest finalne i tablicy nie można modyfikować po utworzeniu obiektu. Dzięki różnym konstruktorom obiekty klasy String
można tworzyć na różne sposoby.
1 2 |
String s = „abc”; String s = new String(„abc”); |
Łańcuchy w Javie zawsze są przechowywane w klasie String
w związku z tym bez problemu można je przypisać do zmiennej.
Konstruktory
Konstruktor bezargumentowy wygląda następująco.
1 2 3 4 |
public String() { this.value = "".value; this.coder = "".coder; } |
Jak widać do pola value przypisywany jest pusty łańcuch. Jest on obiektem typu String
zatem przypisywana jest wartość jego pola value. Nie należy go mylić z wartością null.
Konkatenacja – łączenie Stringów
Skonfrontujmy teraz wiedzę z naszego szkolenia z klasy String
. Pokazywaliśmy tam przecież możliwość łączenia łańcuchów przy pomocy przeciążonego operatora +. Istnieje również metoda o identycznym działaniu concat()
.
1 2 3 4 5 |
String s = new String("Witaj"); System.out.println(s + " na szkoleniu na programistajava.pl"); String s = new String("Witaj"); System.out.println(s.concat(" na szkoleniu na programistajava.pl")); |
W obu przypadkach efektem będzie wyświetlenie komunikatu.
Witaj na szkoleniu na programistajava.pl
Jak widać powyżej połączyliśmy w jeden napis dwa obiekty typu String. Jak to się ma do finalnego pola value? Obiekty typu String zawsze są niemodyfikowalne. Natomiast zarówno użycie operatora + lub metody concat()
powoduje zwrócenie nowego obiektu, który składa się z dwóch uprzednio osobnych łańcuchów.
Aby udowodnić powyższe stwierdzenia wykorzystamy metodę identityHashCode()
z klasy System
. Zaznaczamy, że ta metoda nie zwróci adresu obiektu, a jedynie unikalny kod obiektu. Klasa String posiada swoją implementację metody hashCode()
, która porównuje także treść łańcucha. Z tego względu użyliśmy metody, która działa analogicznie do tej z klasy Object
. Metoda identityHashCode()
jest natywna, co oznacza, że jest napisana w innym języku niż Java. Jest to spowodowane sposobem implementacji, który może zwracać wartość int na podstawie adresu zmiennej. A z poziomu Javy nie ma do niego dostępu.
Spójrzmy zatem na poniższy program.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Test { public static void main(String[]args) { String s = "Programista "; int hashS = System.identityHashCode(s); String s2 = "Java"; s = s + s2; int hashS2 = System.identityHashCode(s); System.out.println("hashS: " + hashS); System.out.println("hashS2: " + hashS2); Object o; } } |
Poniżej pokazujemy wynik wywołania.
hashS: 2055281021
hashS2: 1554547125
Jak widać powyżej wyniki są różne mimo sprawdzenia dokładnie tej samej zmiennej. Z tego wynika, że obiekt s utworzony w miejscu pamięci nie został zmodyfikowany. Kompilator utworzył nowy obiekt złożony z dwóch zmiennych s i s2, a następnie przypisał referencję, do niego pod zmienną s.
StringBuilder, StringBuffer
Powyższy wstęp pokazuje istotną wadę korzystania z klasy String czyli modyfikowanie łańcucha. Jest ona bardzo kosztowna szczególnie przy wielu zmianach (np. pętla). Z pomocą przychodzą klasy StringBuilder
oraz StringBuffer
. Ich działanie jest identyczne. Posiadają dokładnie te same metody. Jedyną różnicą jest synchronizacja między wątkami. Klasa StringBuffer
ją zapewnia, w związku z tym jeśli wiele wątków będzie modyfikować łańcuch należy z niej skorzystać. W przeciwnym wypadku ze względu na szybkość działania polecamy używać StringBuilder
.
Konstruktory
Obie klasy posiadają ten sam zestaw konstruktorów.
StringBuilder()
– tworzy obiekt o pojemności 16.StringBuilder(int capacity)
– konstruuje obiekt o pojemności capacity.StringBuilder(String s)
– tworzy obiekt zawierający łańcuch s o pojemności 16 + długość łańcucha.StringBuilder(CharSequence cs)
– tworzy obiekt zawierający takie same znaki jak cs o pojemności 16 + długość łańcucha.
Metody
StringBuilder append()
– dodaje tekst do istniejącego obiektu. Przyjmuje różne argumenty w tym typy liczbowe, znakowe oraz String.int capacity()
– zwraca rozmiar tablicy wewnętrznejString substring(int start, int end)
– zwraca String, który zawiera sekwencję znaków od start do end.StringBuilder delete(int start, int end)
– usuwa sekwencję znaków w podanym przedziale.String toString()
– zwraca całą sekwencję znaków w postaci String.
Przykłady użycia
Poniżej pokażemy przykład łączenia Stringów w Javie korzystając z obiektu klasy StringBuilder
. Służy do tego metoda append()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public class TestStringBuilder { public static void main(String[]args) { //Dodawanie napisów StringBuilder strObj = new StringBuilder(); strObj.append("Witamy"); strObj.append(" na szkoleniu z Javy"); System.out.println(strObj.toString()); //rozmiar tekstu int n = strObj.length(); //Czyszczenie obiektu strObj.delete(0, n); //Dodawanie wartości liczbowych w pętli for(int i = 0; i < 10; i++) { strObj.append("Powtórzenie: "); strObj.append(i+1); strObj.append("\n"); } System.out.println(strObj.toString()); } } |
Wynik działania programu jest następujący.
1 2 3 4 5 6 7 8 9 10 11 |
Witamy na szkoleniu z Javy Powtórzenie: 1 Powtórzenie: 2 Powtórzenie: 3 Powtórzenie: 4 Powtórzenie: 5 Powtórzenie: 6 Powtórzenie: 7 Powtórzenie: 8 Powtórzenie: 9 Powtórzenie: 10 |
Optymalizowanie pracy z tekstem przy pomocy obiektu StringBuilder
jest szczególnie istotne przy używaniu pętli.
W następnej części przyjrzymy się plikom w Javie.