String - Java
Klasa String służy w Javie do reprezentacji tekstów. W kilku następnych punktach opiszemy wiele możliwości klasy String.
Wskazówka poprawiająca wydajność
Od Javy SE 9 stosuje się bardziej zwięzłą reprezentację obiektów String. Znacząco zmniejszyło się zapotrzebowanie na pamięć w przypadku tekstów zawierających tylko i wyłącznie znaki z zestawu Latin-1, czyli o kodach od 0 do 255. Więcej informacji na ten temat znajdziesz w propozycji JEP 254 dostępnej pod adresem http://openjdk.java.net/jeps/254.
Konstruktory klasy String
Klasa String zapewnia konstruktory do inicjalizacji obiektów String na kilka sposobów. Cztery konstruktory przedstawia metoda main z rysunku 14.1.
2 // Konstruktory klasy String
3
4 public class StringConstructors {
5 public static void main(String[] args) {
6 char[] charArray = {'u', 'r', 'o', 'd', 'z', 'i', 'n', 'y'};
7 String s = new String("witaj");
8
9 // Użycie konstruktorów klasy String
10 String s1 = new String();
11 String s2 = new String(s);
12 String s3 = new String(charArray);
13 String s4 = new String(charArray, 5, 3);
14
15 System.out.printf(
16 "s1 = %s s2 = %s s3 = %s s4 = %s ", s1, s2, s3, s4);
17 }
18 }
s1 =
s2 = witaj
s3 = urodziny
s4 = iny
Rysunek 14.1. Konstruktory klasy String
Wiersz 10. tworzy nowy obiekt String za pomocą konstruktora bezargumentowego i przypisuje go do referencji s1. Nowy obiekt String nie zawiera żadnych znaków (to pusty tekst, który reprezentuje kombinacja "") i ma długość 0. Wiersz 11. tworzy nowy obiekt String za pomocą konstruktora, który przyjmuje inny obiekt String. Przypisuje go następnie do zmiennej s2. Nowy obiekt String zawiera ten sam ciąg znaków co obiekt przekazany jako argument konstruktora.
Wskazówka poprawiająca wydajność
Nie ma potrzeby kopiowania istniejącego obiektu String. Obiekty String są niezmienne, ponieważ klasa String nie oferuje żadnych metod pozwalających na zmianę zawartości obiektu po jego utworzeniu. W praktyce konstruktory klasy String wywołuje się niezwykle rzadko.
Wiersz 12. tworzy nowy obiekt String i przypisuje go do zmiennej s3. Konstruktor tworzy obiekt na podstawie tablicy ze znakami. Nowy obiekt zawiera wszystkie znaki przekazanej tablicy. Wiersz 13. tworzy nowy obiekt String i przypisuje go do zmiennej s4. Konstruktor przyjmuje tablicę ze znakami i dwie liczby całkowite. Drugi argument określa położenie początkowe (offset), od którego pobierane są znaki tablicy. Przypomnijmy, że pierwszy znak ma indeks równy 0. Trzeci argument określa liczbę znaków do pobrania z tablicy. Nowy obiekt String powstaje z tak pobranych znaków. Jeśli offset lub liczba znaków do pobrania powoduje wyjście poza
zakres tablicy znaków, konstruktor zgłosi wyjątek StringIndexOutOfBoundsException.
Metody length, charAt i getChars
Metody length, charAt i getChars klasy String zwracają odpowiednio długość tekstu, znak na zadanej pozycji i znaki tekstu jako tablicę znaków (typ char). Rysunek 14.2 przedstawia każdą z tych metod.
2 // Metody length, charAt i getChars
3 // klasy String
4
5 public class StringMiscellaneous {
6 public static void main(String[] args) {
7 String s1 = "witaj tutaj";
8 char[] charArray = new char[5];
9
10 System.out.printf("s1: %s", s1);
11
12 // Test metody length
13 System.out.printf(" Długość s1: %d", s1.length());
14
15 // Przejdź w pętli przez znaki s1 za pomocą charAt i wyświetl je w odwrotnej kolejności
16 System.out.printf("%nTekst po odwróceniu: ");
17
18 for (int count = s1.length() - 1; count >= 0; count--) {
19 System.out.printf("%c ", s1.charAt(count));
20 }
21
22 // Kopiowanie znaków z tekstu do charArray
23 s1.getChars(0, 5, charArray, 0);
24 System.out.printf("%nTablica znaków: ");
25
26 for (char character : charArray) {
27 System.out.print(character);
28 }
29
30 System.out.println();
31 }
32 }
s1: witaj tutaj
Długość s1: 11
Tekst po odwróceniu: j a t u t j a t i w
Tablica znaków: witaj
Rysunek 14.2. Metody length, charAt i getChars klasy String
Wiersz 13. używa metody length klasy String do określenia liczby znaków znajdujących się w obiekcie String o referencji s1. Podobnie jak tablice, także teksty znają swoją długość. Jednak w odróżnieniu od tablic pobranie długości wymaga użycia metody o nazwie length. Wiersze od 18. do 20. wyświetlają znaki tekstu s1 w odwrotnej kolejności (i po oddzieleniu spacjami). Metoda charAt klasy String (wiersz 19.) zwraca znak na określonej pozycji. Metoda ta przyjmuje argument w postaci liczby całkowitej stanowiący indeks znaku i zwraca ten znak. Podobnie jak w przypadku tablic, numeracja indeksów rozpoczyna się od 0.
Wiersz 23. używa metody getChars do skopiowania znaków tekstu do tablicy znaków. Pierwszym argumentem jest indeks pierwszego znaku do skopiowania. Drugim argumentem jest indeks znaku po ostatnim z kopiowanych znaków. Trzecim argumentem jest tablica, w której mają być umieszczone znaki. Ostatni argument to indeks w tablicy znaków, od którego mają być umieszczane znaki. Wiersze od 26. do 28. wyświetlają zawartość tablicy znak po znaku.
Porównywanie tekstów
Najczęściej sortowane lub wyszukiwane elementy dotyczą tekstów, które trzeba porównywać w celu stwierdzenia ich kolejności lub określenia, czy tekst znajduje się w tablicy (lub innej kolekcji). Klasa String zapewnia metody do porównywania tekstów, co ilustrują dwa następne przykłady.
Aby zrozumieć, co to oznacza, że jeden tekst jest większy lub mniejszy od drugiego, przyjrzyjmy się procesowi układania nazwisk w kolejności alfabetycznej. Z pewnością nazwisko Kowalski umieścimy przed nazwiskiem Nowak, ponieważ pierwsza litera nazwiska Kowalski znajduje się w alfabecie przed pierwszą literą nazwiska Nowak. Alfabet to jednak coś więcej niż 26 podstawowych liter — to uporządkowana lista znaków. Każda litera znajduje się na ściśle określonej pozycji listy. „Z” to coś więcej niż litera alfabetu — to w alfabecie angielskim 26. litera alfabetu.
Skąd komputer wie, która litera znajduje się przed inną? Wszystkie znaki używane przez komputer mają przypisane im kody numeryczne (dodatek B). Gdy komputer porównuje teksty, tak naprawdę porównuje kody numeryczne znaków, z których składa się tekst. Rysunek 14.3 ilustruje metody equals, equalsIgnoreCase, compareTo i regionMatches, a także używa operatora == do porównania tekstów.
2 // Metody equals, equalsIgnoreCase, compareTo i regionMatches
3
4 public class StringCompare {
5 public static void main(String[] args) {
6 String s1 = new String("witaj"); // s1 to kopia "witaj"
7 String s2 = "do widzenia";
8 String s3 = "Wszystkiego Najlepszego";
9 String s4 = "wszystkiego najlepszego";
10
11 System.out.printf(
12 "s1 = %s s2 = %s s3 = %s s4 = %s ", s1, s2, s3, s4);
13
14 // Test równości
15 if (s1.equals("witaj")) { // Prawda
16 System.out.println("s1 jest równe "witaj"");
17 }
18 else {
19 System.out.println("s1 nie jest równe "witaj"");
20 }
21
22 // Test równości za pomocą ==
23 if (s1 == "witaj") { // Fałsz; to nie jest ten sam obiekt
24 System.out.println("s1 to ten sam obiekt co "witaj"");
25 }
26 else {
27 System.out.println("s1 to nie ten sam obiekt co "witaj"");
28 }
29
30 // Test równości (ignorowanie wielkości liter)
31 if (s3.equalsIgnoreCase(s4)) { // Prawda
32 System.out.printf("%s jest równe %s po zignorowaniu wielkości liter ", s3, s4);
33 }
34 else {
35 System.out.println("s3 nie jest równe s4");
36 }
37
38 // Test compareTo
39 System.out.printf(
40 " s1.compareTo(s2) jest %d", s1.compareTo(s2));
41 System.out.printf(
42 " s2.compareTo(s1) jest %d", s2.compareTo(s1));
43 System.out.printf(
44 " s1.compareTo(s1) jest %d", s1.compareTo(s1));
45 System.out.printf(
46 " s3.compareTo(s4) jest %d", s3.compareTo(s4));
47 System.out.printf(
48 " s4.compareTo(s3) jest %d ", s4.compareTo(s3));
49
50 // Test regionMatches (uwzględnianie wielkość liter)
51 if (s3.regionMatches(0, s4, 0, 5)) {
52 System.out.println("Pierwsze 5 znaków s3 i s4 jest sobie równe");
53 }
54 else {
55 System.out.println(
56 "Pierwsze 5 znaków s3 i s4 nie jest sobie równe");
57 }
58
59 // Test regionMatches (ignorowanie wielkości liter)
60 if (s3.regionMatches(true, 0, s4, 0, 5)) {
61 System.out.println(
62 "Pierwsze 5 znaków s3 i s4 jest sobie równe po zignorowaniu wielkości liter");
63 }
64 else {
65 System.out.println(
66 "Pierwsze 5 znaków s3 i s4 nie jest sobie równe po zignorowaniu wielkości liter");
67 }
68 }
69 }
s2 = do widzenia
s3 = Wszystkiego Najlepszego
s4 = wszystkiego najlepszego
s1 jest równe "witaj"
s1 to nie ten sam obiekt co "witaj"
Wszystkiego Najlepszego jest równe wszystkiego najlepszego po zignorowaniu wielkości liter
s1.compareTo(s2) jest 19
s2.compareTo(s1) jest -19
s1.compareTo(s1) jest 0
s3.compareTo(s4) jest -32
s4.compareTo(s3) jest 32
Pierwsze 5 znaków s3 i s4 nie jest sobie równe
Pierwsze 5 znaków s3 i s4 jest sobie równe po zignorowaniu wielkości liter
Rysunek 14.3. Metody equals, equalsIgnoreCase, compareTo i regionMatches klasy String
Metoda equals klasy String
Wiersz 15. używa metody equals (metoda klasy Object przesłaniana przez klasę String), aby porównać obiekt s1 typu String z literałem "witaj" pod kątem równości. Dla tekstów metoda określa, czy ich zawartość jest identyczna. Jeśli tak, zwraca true (prawdę), w przeciwnym razie zwraca false (fałsz). W przedstawionym przykładzie warunek jest prawdziwy, bo s1 zostało zainicjalizowane tekstem "witaj". Metoda equals używa porównania leksykograficznego — porównuje wartości Unicode każdego znaku tekstu. Jeśli więc porównamy "witaj" z "WITAJ", otrzymamy false, ponieważ reprezentacje liczbowe małych liter różnią się od reprezentacji liczbowych dużych liter.
Porównywanie tekstów operatorem ==
Warunek w wierszu 23. używa operatora == do porównania obiektu s1 z literałem "witaj". Gdy porównuje się typy podstawowe za pomocą ==, wynik jest równy true, jeśli obie wartości są identyczne. Gdy jednak porównuje się referencje, wynik jest równy true, jeśli obie referencje odnoszą się do tego samego obiektu w pamięci. Aby porównać faktyczną zawartość (stan) obiektów pod kątem
równości, trzeba wywołać metodę. W przypadku tekstów metodą tą jest equals. Warunek z wiersza 23. zwraca false, ponieważ referencja s1 została zainicjalizowana w następujący sposób:
s1 = new String("witaj");
co spowodowało utworzenie nowego obiektu String z kopią literału "witaj" i przypisanie go do zmiennej s1. Gdyby s1 było inicjalizowane poleceniem:
s1 = "witaj";
które bezpośrednio przypisuje literał "witaj" do zmiennej s1, warunek byłby spełniony. Pamiętaj, że Java traktuje wszystkie obiekty literałów tekstowych o tej samej zawartości jako ten sam obiekt String o wielu referencjach. Oznacza to, że literały "witaj" z wierszy 6., 15. i 23. odnoszą się do tego samego obiektu String.
Typowy błąd programistyczny
Porównywanie referencji za pomocą operatora == może prowadzić do błędów logicznych, ponieważ == porównuje referencje, aby stwierdzić, czy to ten sam obiekt, a nie czy dwa obiekty mają tę samą zawartość. Jeśli dwa różne obiekty mają tę samą treść, ale zostaną porównane operatorem ==, wynikiem zawsze będzie wartość false. Aby sprawdzić, czy dwa obiekty mają tę samą zawartość, użyj metody equals.
Metoda equalsIgnoreCase klasy String
W trakcie sortowania tekstów można porównywać wartości pod kątem równości z użyciem metody equalsIgnoreCase, która sprawdza teksty bez uwzględniania wielkości liter. W takim porównaniu "witaj" i "WITAJ" to ten sam tekst. Wiersz 31. porównuje tekst "Wszystkiego Najlepszego" ze zmiennej s3 z tekstem "wszystkiego najlepszego" ze zmiennej s4. Wynik porównania zwraca true, ponieważ wielkość liter nie ma znaczenia.
Metoda compareTo klasy String
Wiersze od 39. do 48. używają metody compareTo do porównywania tekstów. Klasa String implementuje interfejs Comparable, który deklaruje metodę compareTo. Wiersz 40. porównuje String s1 ze String s2. Metoda compareTo zwraca 0, jeśli teksty są sobie równe; wartość ujemną, jeśli obiekt String wywołujący compareTo jest mniejszy od tekstu przekazanego jako argument; wartość dodatnią, jeśli obiekt String wywołujący compareTo jest większy od tekstu przekazanego jako argument. Metoda compareTo używa porządku leksykograficznego — porównuje wartości odpowiadające poszczególnym znakom obu tekstów.
Metoda regionMatches klasy String
Warunek w wierszu 51. używa metody regionMatches klasy String do porównania fragmentów dwóch tekstów pod kątem równości. Pierwszym argumentem metody jest indeks początku tekstu w obiekcie String, dla którego wywołano metodę. Drugim argumentem jest porównywany tekst. Trzeci argument to indeks początku do porównania w przekazanym tekście. Ostatni argument to
liczba znaków do porównania. Metoda zwraca true, jeśli wskazane znaki obu tekstów są równe leksykograficznie. Warunek w wierszu 60. używa pięcioargumentowej wersji metody regionMatches klasy String. Jeśli pierwszym argumentem jest wartość true, metoda ignoruje wielkość znaków w trakcie ich porównywania. Pozostałe argumenty są takie same jak w wersji czteroargumentowej metody regionMatches.
Metody startsWith i endsWith klasy String
Rysunek 14.4 przedstawia metody startsWith i endsWith klasy String. Metoda main tworzy tablicę z tekstami "zajęte", "zajęta", "wolne" i "aktywne". Pozostała część metody składa się z trzech instrukcji for, które sprawdzają, czy elementy tablicy zaczynają się lub kończą określonymi zestawami znaków.
2 // Metody startsWith i endsWith klasy String
3
4 public class StringStartEnd {
5 public static void main(String[] args) {
6 String[] strings = {"zajęte", "zajęta", "wolne", "aktywne"};
7
8 // Test metody startsWith
9 for (String string : strings) {
10 if (string.startsWith("za")) {
11 System.out.printf(""%s" zaczyna się od "za" ", string);
12 }
13 }
14
15 System.out.println();
16
17 // Test metody startsWith z początkiem sprawdzania od znaku 2.
18 for (String string : strings) {
19 if (string.startsWith("jęt", 2)) {
20 System.out.printf(
21 ""%s" zaczyna się od "jęt" na pozycji 2. ", string);
22 }
23 }
24
25 System.out.println();
26
27 // Test metody endsWith
28 for (String string : strings) {
29 if (string.endsWith("ne")) {
30 System.out.printf(""%s" kończy się na "ne" ", string);
31 }
32 }
33 }
34 }
"zajęta" zaczyna się od "za"
"zajęte" zaczyna się od "jęt" na pozycji 2.
"zajęta" zaczyna się od "jęt" na pozycji 2.
"wolne" kończy się na "ne"
"aktywne" kończy się na "ne"
Rysunek 14.4. Metody startsWith i endsWith klasy String
Wiersze od 9. do 13. używają wersji metody startsWith, która przyjmuje tylko jeden argument w postaci tekstu. Warunek w instrukcji if (wiersz 10.) określa, czy każdy tekst w tablicy zaczyna się od znaków "za". Jeśli tak, metoda zwraca true i aplikacja wyświetla tekst. Jeśli metoda zwróci false, nic się nie dzieje. Wiersze od 18. do 23. używają wersji metody startsWith, która przyjmuje dwa
argumenty: obiekt String i liczbę całkowitą. Liczba całkowita określa indeks, od którego powinno rozpocząć się wyszukiwanie. Warunek w instrukcji if (wiersz 19.) określa, czy każdy tekst w tablicy zawiera znaki "jęt" od trzeciego znaku tekstu (indeks 2). Jeśli tak, metoda zwraca true i aplikacja wyświetla tekst. Trzecia instrukcja for (wiersze od 28. do 32.) używa metody endsWith, która
przyjmuje argument w postaci tekstu. Warunek w wierszu 29. określa, czy każdy tekst w tablicy kończy się znakami "ne". Jeśli tak, metoda zwraca true i aplikacja wyświetla tekst.
Programowanie w Javie. Solidna wiedza w praktyce. Wydanie XI, Autorzy: Paul Deitel, Harvey Deitel, Wydawnictwo: Helion