Ten artykuł ma za zadanie pomóc Ci w zrozumieniu koncepcji podciągów w języku Java. Oprócz teoretycznych wyjaśnień, zaprezentujemy praktyczne przykłady kodu, które ułatwią Ci wizualizację i przyswojenie wiedzy. Nauczymy Cię, jak tworzyć podciągi i jak wyszukiwać je w obrębie łańcucha znaków.
Zanim jednak przejdziemy do praktyki, warto zapoznać się z fundamentalnymi pojęciami dotyczącymi łańcuchów i podciągów.
Czym są łańcuchy znaków i podciągi?
W kontekście języka Java, łańcuch znaków to uporządkowana sekwencja znaków. Każdy napis w Javie jest traktowany jako obiekt. Łańcuch znaków może składać się z liter, symboli, a nawet spacji. Z kolei podciąg w Javie to fragment lub podzbiór danego łańcucha.
Przykładowo, „Geek” jest podciągiem łańcucha „newsblog.pl”. Podciągi umożliwiają wyodrębnienie konkretnej części łańcucha.
Jeżeli masz dane „Jan Kowalski” i chcesz uzyskać tylko imię „Jan”, możesz to łatwo osiągnąć za pomocą podciągów. Podobnie, jeśli posiadasz listę nazwisk „Jan, Jacek, Julia” i chcesz sprawdzić, czy na liście znajduje się „Jan”, również możesz wykorzystać do tego podciągi. To tylko nieliczne przykłady. Znajomość operacji na podciągach pozwala na ich wykorzystanie w różnorodnych sytuacjach.
Skoro już wiemy, czym są podciągi w Javie, przejdźmy do nauki ich tworzenia i pracy z nimi.
# 1. Metoda „substring()”
Metoda „substring()” jest bardzo pomocna przy tworzeniu podciągów. Przyjmuje jeden lub dwa parametry wejściowe: startIndex (indeks początkowy) lub zarówno startIndex, jak i endIndex (indeks końcowy), i zwraca odpowiedni podciąg.
W zależności od liczby parametrów, możemy ją stosować na dwa sposoby. Przyjrzyjmy się im bliżej.
substring (int startIndex)
Najprostsza forma metody to „substring(startIndex)”. Metoda ta przyjmuje jako argument liczbę całkowitą reprezentującą pozycję początkową podciągu. Zwraca ona podciąg, który rozpoczyna się od podanego indeksu i rozciąga się do końca pierwotnego łańcucha.
Oto przykład:
public class Substrings{ public static void main(String args[]){ String str="newsblog.pl"; System.out.println("Dany ciąg: " + str); System.out.println("Podciąg: " +str.substring(4)); //indeksowanie łańcuchów zaczyna się od 0 } }
WYJŚCIE:
Dany ciąg: newsblog.pl Podciąg: sblog.pl
Z powyższego wyniku widać, że z łańcucha „newsblog.pl” uzyskaliśmy podciąg „sblog.pl”. Podciąg zaczyna się od indeksu 4 (czyli od piątej litery) i obejmuje wszystkie znaki aż do końca łańcucha.
substring(int startIndex, int endIndex)
To druga możliwość użycia metody substring klasy String. Metodzie substring możemy przekazać dwie liczby całkowite – indeks początkowy i indeks końcowy. Aby z niej skorzystać, należy użyć formatu „substring(startIndex,endIndex)”.
Dla lepszego zrozumienia, spójrzmy na przykład:
public class Substrings{ public static void main(String args[]){ String str="GeekFlareFans"; System.out.println("Dany ciąg: " + str); System.out.println("Podciąg: " +str.substring(4,9)); //Podciąg od indeksu 4 do 8. } }
WYJŚCIE:
Dany ciąg: GeekFlareFans Podciąg: Flare
W efekcie, z łańcucha „GeekFlareFans” uzyskujemy podciąg „Flare”. Podaliśmy indeks początkowy 4 i indeks końcowy 9. Podciąg zaczyna się od elementu o indeksie 4, a kończy przed indeksem 9. Ważne jest, że element o indeksie końcowym nie jest uwzględniony w podciągu. Podciąg zawiera więc wszystkie elementy do indeksu końcowego, wyłączając sam element o tym indeksie.
#2. Metoda „split()”
Metoda „split()” to kolejna metoda klasy String w Javie, która umożliwia tworzenie podciągów. Jest ona szczególnie użyteczna, gdy informacje są przechowywane w jednym łańcuchu, a poszczególne elementy są oddzielone wspólnym separatorem.
W składni pojawia się termin „regex”, który na pierwszy rzut oka może wydawać się skomplikowany. Wyjaśnijmy więc, czym jest regex. To skrót od „wyrażenia regularne”. Wyrażenie regularne to ciąg znaków opisujący pewien wzorzec w tekście. W kontekście metody split, separatorem jest właśnie wyrażenie regularne.
Metoda „split()” może przyjmować do dwóch argumentów: wyrażenie regularne (separator) i opcjonalnie limit (liczba całkowita). Wyrażenie regularne wyznacza miejsce podziału łańcucha na podciągi. Oryginalny łańcuch zostanie podzielony na dwie części: przed i po wystąpieniu wyrażenia regularnego.
Na przykład, jeśli spróbujemy podzielić łańcuch „abcdef” używając „bcd” jako wyrażenia regularnego, w rezultacie otrzymamy podciągi „a” i „ef”.
Metoda zwraca tablicę z podzielonymi łańcuchami. Możemy określić tylko wyrażenie regularne lub zarówno wyrażenie regularne, jak i limit. Przeanalizujmy różne sposoby wywoływania tej metody.
split(wyrażenie regularne)
Pierwsza wersja metody przyjmuje tylko wyrażenie regularne, czyli format „split(regex)”. Nie ma tu parametru limitu, dlatego metoda zwraca tablicę zawierającą wszystkie oddzielone podciągi.
Aby lepiej to zrozumieć, spójrzmy na kod:
public class Substrings{ public static void main(String args[]){ String str="Geek%Flare"; String[] substrings=str.split("%"); System.out.println("Dany ciąg: " + str); System.out.println("Pierwszy podciąg: " + substrings[0]); System.out.println("Drugi podciąg: " + substrings[1]); } }
WYJŚCIE:
Dany ciąg: Geek%Flare Pierwszy podciąg: Geek Drugi podciąg: Flare
Z kodu wynika, że dany łańcuch zawiera separator regex „%”. Nie musi to być jeden znak, może to być dowolny ciąg o dowolnej długości. Metoda „split()” ignoruje to wyrażenie regularne i zwraca wszystkie łańcuchy, które były nim oddzielone. Podciągi są umieszczane w tablicy.
W przykładzie, dany ciąg to „Geek%Flare”. Otrzymujemy tablicę z dwoma elementami: „Geek” i „Flare”. Następnie uzyskujemy dostęp do tych elementów za pomocą indeksów (0 i 1) i wypisujemy je na konsoli.
Warto wspomnieć, że jeśli metoda zostanie wywołana bez parametrów, zostanie zgłoszony błąd. Natomiast, jeśli podamy pusty ciąg („”) jako wyrażenie regularne, otrzymamy każdy znak jako oddzielny podciąg. Przeanalizujmy przykład, aby lepiej to zobrazować.
import java.util.Arrays; public class Substrings{ public static void main(String args[]){ String str="Geek%Flare"; String[] substrings=str.split(""); System.out.println(Arrays.toString(substrings)); } }
WYJŚCIE:
[G, e, e, k, %, F, l, a, r, e]
Jak widzimy, w tym przykładzie parametr regex jest pustym łańcuchem, co powoduje, że metoda zwraca każdy znak jako oddzielny podciąg, co jest widoczne po wyświetleniu tablicy wyjściowej metody „split()”.
split (wyrażenie regularne, limit int)
Druga wersja tej metody daje większą kontrolę nad wynikami. Metoda „split()” pobiera teraz dwa parametry: wyrażenie regularne i limit, zgodnie z formatem „split(regex, limit)”.
„Limit” to maksymalna liczba podciągów, które mają zostać zwrócone. W zależności od wartości limitu, możemy wyróżnić trzy przypadki:
Przypadek 1: Jeżeli limit>0, wynikowa tablica będzie zawierała podciągi, przy czym podział zostanie zastosowany co najwyżej (limit-1) razy. Oznacza to, że tablica nie będzie zawierała więcej elementów niż podany limit, a pozostała część łańcucha, która nie została podzielona, zostanie zachowana w niezmienionej formie. Zobaczmy to na przykładzie:
import java.util.Arrays; public class Substrings{ public static void main(String args[]){ String str="Geek%Flare%is%the%best"; String[] substrings=str.split("%",2); System.out.println(Arrays.toString(substrings)); } }
WYJŚCIE:
[Geek, Flare%is%the%best]
Widzimy, że tablica wynikowa zawiera tylko dwa elementy, zgodnie z wartością podaną w parametrze limit. Zwróćmy uwagę, że podział został zastosowany tylko raz, czyli (limit-1) razy.
Jeżeli jednak wyrażenie regularne występuje dwa razy z rzędu („%%”), spowoduje to powstanie pustych podciągów. Zilustrujmy to fragmentem kodu:
import java.util.Arrays; public class Substrings{ public static void main(String args[]){ String str="Geek%Flare%is%%the%best%%%"; String[] substrings=str.split("%",5); System.out.println(Arrays.toString(substrings)); } }
WYJŚCIE:
[Geek, Flare, is, , the%best%%%]
W skrócie, jeżeli po znaku „%” bezpośrednio następuje kolejny „%” lub koniec łańcucha, tworzy się pusty podciąg.
Przypadek 2: Jeżeli limit<0, podział zostanie zastosowany tyle razy, ile to możliwe, bez ograniczeń na rozmiar tablicy. Tablica będzie zawierała puste podciągi, jeśli wyrażenie regularne występuje dwa razy z rzędu („%%”).
import java.util.Arrays; public class Substrings{ public static void main(String args[]){ String str="Geek%Flare%is%%the%best%%%"; String[] substrings=str.split("%",-1); System.out.println(Arrays.toString(substrings)); } }
WYJŚCIE:
[Geek, Flare, is, , the, best, , , ]
W wyniku widać, że podział został zastosowany maksymalną ilość razy, a tablica zawiera również puste podciągi.
Przypadek 3: Jeżeli limit=0, podział zostanie zastosowany tyle razy, ile to możliwe, ale wszystkie puste podciągi na końcu łańcucha zostaną pominięte w tablicy wynikowej.
import java.util.Arrays; public class Substrings{ public static void main(String args[]){ String str="Geek%Flare%is%%the%best%%%"; String[] substrings=str.split("%",0); System.out.println(Arrays.toString(substrings)); } }
WYJŚCIE:
[Geek, Flare, is, , the, best]
Wyniki są bardzo podobne jak w przypadku limit=-1, ale nie zawierają pustych podciągów na końcu. Innymi słowy, puste podciągi na końcu tablicy są ignorowane.
Warto także zauważyć, że jeżeli wyrażenie regularne nie występuje w łańcuchu, metoda zwróci cały oryginalny łańcuch.
Sprawdzanie, czy łańcuch zawiera podciąg
Oprócz tworzenia podciągów, możemy również zidentyfikować podciąg i sprawdzić, czy istnieje on w danym łańcuchu. To szybki i łatwy sposób, który przydaje się w wielu sytuacjach. Jak to zrobić? Pomogą nam różne metody. Prześledźmy je po kolei.
Metoda „contains()”
Za pomocą metody „contains()” możemy bardzo łatwo sprawdzić, czy dany podciąg istnieje w łańcuchu. Metoda ta klasy String przyjmuje jako argument ciąg znaków, który jest naszym podciągiem, i zwraca wartość logiczną (true lub false), w zależności od tego, czy podciąg znajduje się w łańcuchu, czy nie. Metoda może być używana w instrukcjach warunkowych if-else, operatorach jednoargumentowych i w wielu innych miejscach, gdzie potrzebna jest bardziej zaawansowana logika.
Przyjrzyjmy się bliżej tej metodzie:
public class Substrings{ public static void main(String args[]){ String str="newsblog.pl"; System.out.println("Czy zawiera Flare? \n"+ str.contains("Flare")); } }
WYJŚCIE:
Czy zawiera Flare? true
Powyższy kod sprawdza, czy łańcuch „newsblog.pl” zawiera słowo „Flare”. Ponieważ je zawiera, zwracana jest wartość logiczna „true”, co potwierdza obecność podciągu.
public class Substrings{ public static void main(String args[]){ String str="newsblog.pl"; System.out.println("Czy zawiera Flare? \n"+ str.contains("Flare1")); } }
WYJŚCIE:
Czy zawiera Flare? false
Z tego przykładu rozumiemy, że jeżeli podciąg nie występuje w łańcuchu, metoda zwróci wartość false, co sygnalizuje brak podciągu. W ten sposób łatwo możemy zweryfikować istnienie podciągu.
Znajdowanie pozycji podciągu
# 1. Metoda „indexOf()”
Metoda „indexOf()” może być wykorzystana do znalezienia podciągu, jak również do uzyskania informacji o jego indeksie. Metoda pobiera jako argument łańcuch znaków lub znak i zwraca pozycję jego pierwszego wystąpienia. Niestety, podaje tylko indeks pierwszego wystąpienia i nie informuje, czy dany podciąg pojawia się w łańcuchu wielokrotnie. W przypadku, gdy podciąg nie występuje, metoda zwraca -1.
Przyjrzyjmy się bliżej tej metodzie.
public class Substrings{ public static void main(String args[]){ String str="GeekFlareGeekFlare"; System.out.println("Indeks Flare: "+ str.indexOf("Flare")); } }
WYJŚCIE:
Indeks Flare: 4
W tym przykładzie, pierwsze wystąpienie podciągu „Flare” zaczyna się od indeksu 4 w łańcuchu „GeekFlareGeekFlare”. Zgodnie z oczekiwaniami, funkcja zwróciła ten indeks.
#2. Metoda „lastIndexOf()”
Metoda `lastIndexOf()` jest bardzo podobna do `indexOf()`. Obie metody pobierają jako argument podciąg i zwracają indeks jego pozycji. Mają też taką samą wartość zwracaną, gdy podciąg nie zostanie znaleziony w łańcuchu. Obie zwracają -1 w przypadku niepowodzenia.
Natomiast, podczas gdy `indexOf()` zwraca indeks pierwszego wystąpienia podciągu, `lastIndexOf()` zwraca indeks jego ostatniego wystąpienia.
Zobaczmy to na przykładzie kodu:
public class Substrings{ public static void main(String args[]){ String str="GeekFlareGeekFlare"; System.out.println("Ostatni indeks Flare: "+ str.lastIndexOf("Flare")); } }
WYJŚCIE:
Ostatni indeks Flare:13
Analizując ten wynik, widzimy, że metoda `lastIndexOf()` działa zgodnie z przeznaczeniem i zwraca indeks ostatniego wystąpienia podciągu „Flare” w łańcuchu „GeekFlareGeekFlare”.
Najczęściej zadawane pytania
Jak używać metody „split()” do tworzenia niepustych podciągów?
Jeżeli w głównym łańcuchu występuje wiele wystąpień wyrażenia regularnego jedno po drugim (np. „Hello%%Hi”, gdzie wyrażeniem regularnym jest „%”), metoda „split()” potraktuje pierwsze wystąpienie jako znak podziału, a pozostałe zwróci jako pusty łańcuch. Aby tego uniknąć, możemy określić parametr limit jako 0, dzięki czemu w wynikach zostaną zwrócone tylko niepuste łańcuchy.
Czy metoda „indexOf()” zwraca indeksy wszystkich wystąpień podciągu?
Nie, „indexOf()” nie zwraca indeksów wszystkich wystąpień podciągu. Metoda zwraca tylko indeks pierwszego wystąpienia podciągu. Jeżeli podciąg nie zostanie znaleziony, metoda zwróci -1.
Co zwraca metoda „substring()”, jeżeli podane indeksy nie istnieją w łańcuchu?
Jeżeli podane indeksy (początkowy i końcowy) nie istnieją w łańcuchu, kompilator zgłosi błąd. Błąd będzie zawierał komunikat „java.lang.StringIndexOutOfBoundsException: ” i program nie zostanie wykonany.
Podsumowanie
W tym artykule omówiliśmy różne metody i podstawy, które pomogą Ci rozpocząć pracę z podciągami. Nauczyliśmy się tworzyć podciągi oraz sprawdzać, czy podciąg występuje w łańcuchu. Ta wiedza pomoże Ci lepiej zrozumieć, jak pracować z podciągami. Przeanalizuj przykłady i ćwicz, aby dobrze opanować tę tematykę.
Następnie możesz przejrzeć naszą listę pytań do rozmowy kwalifikacyjnej z języka Java.