JavaRush /Blog Java /Random-PL /Zamień ciąg w Javie

Zamień ciąg w Javie

Opublikowano w grupie Random-PL
W pracy programisty dość często niektóre zadania lub ich elementy mogą się powtarzać. Dlatego dzisiaj chciałbym poruszyć temat, który często spotyka się w codziennej pracy każdego programisty Java. Zamień ciąg w Javie - 1Załóżmy, że otrzymujesz określony ciąg znaków z określonej metody. I wszystko wydaje się być w porządku, ale jest pewna drobnostka, która Ci nie pasuje. Np. separator się nie nadaje i trzeba jakiś inny (albo wcale). Co można zrobić w takiej sytuacji? Oczywiście skorzystaj z metod replaceklasy String.

Zamień ciąg Java

Obiekt typu Stringma cztery odmiany metody zamiany replace:
  • replace(char, char);
  • replace(CharSequence, CharSequence);
  • replaceFirst(String, String);
  • replaceAll(String, String).
Cel wszystkich tych metod jest ten sam - zastąpienie części ciągu innym ciągiem. Przyjrzyjmy się im bliżej. 1.replace(char, char) String replace(char oldChar, char newChar) - zastępuje wszystkie wystąpienia znaku pierwszego argumentu oldChardrugim - newChar. W tym przykładzie zastąpimy przecinek średnikiem:
String value = "In JavaRush, Diego the best, Diego is Java God".replace(',', ';');
System.out.println(value);
Wyjście konsoli:
In JavaRush; Diego the best; Diego is Java God
2.replace(CharSequence, CharSequence) Zastępuje każdy podciąg ciągu znaków pasujący do określonej sekwencji znaków ciągiem znaków zastępczych.
String value = "In JavaRush, Diego the best, Diego is Java God".replace("Java", "Rush");
System.out.println(value);
Wniosek:
In RushRush, Diego the best, Diego is Rush God
3.replaceFirst(String, String) String replaceFirst(String regex, String replacement) - Zastępuje pierwszy podciąg pasujący do określonego wyrażenia regularnego ciągiem zastępczym. Używając nieprawidłowego wyrażenia regularnego, możesz przechwycić wyjątek PatternSyntaxException (co nie jest dobrą rzeczą). W tym przykładzie zamieńmy nazwę robota-mistrza:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceFirst("Diego", "Amigo");
System.out.println(value);
Wyjście konsoli:
In JavaRush, Amigo the best, Diego is Java God
Jak widać, zmianie uległo jedynie pierwsze wystąpienie „Diego”, natomiast kolejne pozostały pominięte, czyli nietknięte. 4. replaceAll()w Javie String replaceAll(String regex, String replacement) - ta metoda zastępuje wszystkie wystąpienia podciągu w ciągu regexznaków replacement. Jako pierwszego argumentu można użyć wyrażenia regularnego regex. Jako przykład spróbujmy wykonać poprzednią zamianę nazwami, ale nową metodą:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("Diego", "Amigo");
System.out.println(value);
Wyjście konsoli:
In JavaRush, Amigo the best, Amigo is Java God
Jak widzimy, wszystkie symbole zostały całkowicie zastąpione niezbędnymi. Myślę, że Amigo będzie zadowolony =)

Wyrażenia regularne

Powyżej powiedziano, że można zastąpić wyrażeniem regularnym. Najpierw wyjaśnijmy sobie, czym jest wyrażenie regularne? Wyrażenia regularne to formalny język służący do wyszukiwania i manipulowania podciągami w tekście w oparciu o użycie metaznaków (znaków wieloznacznych). Mówiąc najprościej, jest to wzór znaków i metaznaków definiujący regułę wyszukiwania. Na przykład: \D- szablon opisujący dowolny znak niecyfrowy; \d— definiuje dowolny znak numeryczny, który można również opisać jako [0-9]; [a-zA-Z]— szablon opisujący znaki łacińskie od a do z, wielkość liter nie jest uwzględniana; Rozważ zastosowanie w metodzie replaceAllklasowej String:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("\\s[a-zA-Z]{5}\\s", " Amigo ");
System.out.println(value);
Wyjście konsoli:
In JavaRush, Amigo the best, Amigo is Java God
\\s[a-zA-Z]{5}\\s— opisuje słowo składające się z 5 znaków łacińskich otoczonych spacjami. Odpowiednio szablon ten zostaje zastąpiony przekazanym przez nas ciągiem znaków.

Zamień wyrażenie regularne Java

Zasadniczo, aby używać wyrażeń regularnych w Javie, należy wykorzystać możliwości platformy java.util.regex. Kluczowe klasy to:
  1. Pattern- klasa udostępniająca skompilowaną wersję wyrażenia regularnego.
  2. Matcher— ta klasa interpretuje wzorzec i określa dopasowania w otrzymanym ciągu znaków.
Zazwyczaj te dwie klasy działają w połączeniu. Jak więc będzie wyglądał nasz poprzedni obiekt, ale za pomocą Matcheri Pattern:
Pattern pattern = Pattern.compile("\\s[a-zA-Z]{5}\\s");
Matcher matcher = pattern.matcher("In JavaRush, Diego the best, Diego is Java God");
String value = matcher.replaceAll(" Amigo ");
System.out.println(value);
A nasz wniosek będzie taki sam:
In JavaRush, Amigo the best, Amigo is Java God
Więcej o wyrażeniach regularnych możesz przeczytać w tym artykule .

Alternatywa dla zastąpieniaWszystko

Nie ulega wątpliwości, że metody replacerobią Stringwrażenie, jednak nie można ignorować faktu, że Stringjest to immutableobiekt, czyli nie można go zmienić po stworzeniu. Dlatego też, gdy za pomocą metod zastępujemy pewne części ciągu znaków replace, nie zmieniamy obiektu String, lecz za każdym razem tworzymy nowy, z niezbędną zawartością. Ale tworzenie nowego obiektu za każdym razem zajmuje dużo czasu, prawda? Zwłaszcza, gdy nie chodzi o kilka obiektów, ale o kilkaset, a nawet tysiące. Chcąc nie chcąc, zaczynasz myśleć o alternatywach. A jakie mamy alternatywy? Zamień ciąg w Javie - 2Hmm... Jeśli chodzi o Stringjego właściwość immutable, od razu przychodzą ci na myśl alternatywy, ale nie immutable, a mianowicie StringBuilder/StringBuffer . Jak pamiętamy, klasy te właściwie niczym się nie różnią, poza tym, że StringBuffersą zoptymalizowane do użycia w środowisku wielowątkowym, więc StringBuilderw przypadku użycia jednowątkowego działają nieco szybciej. Na tej podstawie dzisiaj skorzystamy z StringBuilder. tej klasy, która ma wiele ciekawych metod, ale konkretnie teraz interesuje nas replace. StringBuilder replace(int start, int end, String str)— ta metoda zastępuje znaki w podciągu tej sekwencji znakami z określonego ciągu. Podciąg zaczyna się od określonego początku i trwa aż do znaku na końcu indeksu -1lub do końca sekwencji, jeśli taki znak nie istnieje. Spójrzmy na przykład:
StringBuilder strBuilder = new StringBuilder("Java Rush");
strBuilder.replace(5, 9, "God");
System.out.println(strBuilder);
Wniosek:
Java God
Jak widać, wskazujemy przedział, w którym chcemy zapisać ciąg, i dopisujemy podciąg na wierzchu tego, co znajduje się w przedziale. Korzystając z pomocy, StringBuilderodtworzymy analogię metody replaceall java. Jak to będzie wyglądać:
public static String customReplaceAll(String str, String oldStr, String newStr) {

   if ("".equals(str) || "".equals(oldStr) || oldStr.equals(newStr)) {
       return str;
   }
   if (newStr == null) {
       newStr = "";
   }
   final int strLength = str.length();
   final int oldStrLength = oldStr.length();
   StringBuilder builder = new StringBuilder(str);

   for (int i = 0; i < strLength; i++) {
       int index = builder.indexOf(oldStr, i);

       if (index == -1) {
           if (i == 0) {
               return str;
           }
           return builder.toString();
       }
       builder = builder.replace(index, index + oldStrLength, newStr);

   }
       return builder.toString();
}
Na pierwszy rzut oka to przerażające, ale przy odrobinie zrozumienia można zrozumieć, że wszystko nie jest takie skomplikowane i całkiem logiczne.Mamy trzy argumenty:
  • str— linia, w której chcemy zastąpić niektóre podciągi;
  • oldStr— reprezentacja podciągów, które zastąpimy;
  • newStr- czym to zastąpimy.
Pierwszy z nich ifjest nam potrzebny do sprawdzania przychodzących danych i jeśli ciąg znaków strbędzie oldStrpusty lub nowy podciąg newStrbędzie równy staremu oldStr, to wykonanie metody będzie pozbawione sensu. Dlatego zwracamy oryginalny ciąg - str. Następnie sprawdzamy , newStrczy nulltak jest i jeśli tak jest, to konwertujemy go na wygodniejszy dla nas format pustego ciągu znaków - "". Następnie mamy deklarację potrzebnych nam zmiennych:
  • całkowita długość ciągu str;
  • długość podciągu oldStr;
  • obiekt StringBuilderze wspólnego ciągu.
Rozpoczynamy pętlę, która powinna zostać wykonana tyle razy, ile wynosi długość całego łańcucha (ale najprawdopodobniej nigdy tak się nie stanie). Stosując metodę klasową StringBuilder- indexOf- znajdujemy indeks pierwszego wystąpienia interesującego nas podciągu. Niestety chciałbym zaznaczyć, że indexOfnie działa to z wyrażeniami regularnymi, więc nasza ostateczna metoda będzie działać tylko z wystąpieniami ciągów znaków ((Jeśli ten indeks jest równy -1, to nie ma już więcej wystąpień tych wystąpień w bieżącym obiekcie StringBuilder, więc wychodzimy z metody z wynikiem będącym przedmiotem zainteresowania: jest on zawarty w naszym StringBuilder, na które konwertujemy za Stringpomocą toString.Jeśli nasz indeks jest równy -1w pierwszej iteracji pętli, to podciąg, który należy zastąpić, nie znajdował się w ogólnym początkowo string. Dlatego w takiej sytuacji po prostu zwracamy ogólny string. Następnie mamy i opisaną powyżej metodę wykorzystujemy replaceznaleziony StringBuilderindeks wystąpienia do wskazania współrzędnych podciągu, który ma zostać zastąpiony. Ta pętla zostanie uruchomiona tyle razy, ile zostanie znalezionych podciągów, które należy zastąpić.Jeśli ciąg składa się tylko ze znaku, który należy zastąpić, to tylko w tym przypadku mamy Pętla wykona się całkowicie i otrzymamy wynik StringBuilderprzekonwertowany na ciąg. Trzeba sprawdzić poprawność tej metody, prawda? Napiszmy test sprawdzający działanie metody w różnych sytuacjach:
@Test
public void customReplaceAllTest() {
   String str = "qwertyuiop__qwertyuiop__";

   String firstCase = Solution.customReplaceAll(str, "q", "a");
   String firstResult = "awertyuiop__awertyuiop__";
   assertEquals(firstCase, firstResult);

   String secondCase = Solution.customReplaceAll(str, "q", "ab");
   String secondResult = "abwertyuiop__abwertyuiop__";
   assertEquals(secondCase, secondResult);

   String thirdCase = Solution.customReplaceAll(str, "rtyu", "*");
   String thirdResult = "qwe*iop__qwe*iop__";
   assertEquals(thirdCase, thirdResult);

   String fourthCase = Solution.customReplaceAll(str, "q", "");
   String fourthResult = "wertyuiop__wertyuiop__";
   assertEquals(fourthCase, fourthResult);

   String fifthCase = Solution.customReplaceAll(str, "uio", "");
   String fifthResult = "qwertyp__qwertyp__";
   assertEquals(fifthCase, fifthResult);

   String sixthCase = Solution.customReplaceAll(str, "", "***");
   assertEquals(sixthCase, str);

   String seventhCase = Solution.customReplaceAll("", "q", "***");
   assertEquals(seventhCase, "");
}
Można go podzielić na 7 oddzielnych testów, z których każdy będzie odpowiedzialny za własny przypadek testowy. Po uruchomieniu przekonamy się, że jest zielony, czyli udany. Cóż, to chyba wszystko. Chociaż poczekaj, powiedzieliśmy powyżej, że ta metoda będzie znacznie szybsza replaceAllniż String. No cóż, spójrzmy:
String str = "qwertyuiop__qwertyuiop__";
long firstStartTime = System.nanoTime();

for (long i = 0; i < 10000000L; i++) {
   str.replaceAll("tyu", "#");
}

double firstPerformance = System.nanoTime() - firstStartTime;

long secondStartTime = System.nanoTime();

for (long i = 0; i < 10000000L; i++) {
   customReplaceAll(str, "tyu", "#");
}

double secondPerformance = System.nanoTime() - secondStartTime;

System.out.println("Performance ratio  - " +  firstPerformance / secondPerformance);
Następnie ten kod został uruchomiony trzykrotnie i otrzymaliśmy następujące wyniki: Dane wyjściowe konsoli:
Performance ratio  - 5.012148941181627
 
Performance ratio  - 5.320637176017641
 
Performance ratio  - 4.719192686500394
Jak widzimy, nasza metoda jest średnio 5 razy bardziej produktywna niż replaceAllzajęcia klasyczne! StringNo cóż, w końcu przeprowadźmy ten sam test, ale, że tak powiem, na próżno. Innymi słowy, w przypadku, gdy nie zostanie znalezione żadne dopasowanie. Zamieńmy wyszukiwany ciąg z "tyu"na "--". Trzy serie dały następujące wyniki: Dane wyjściowe konsoli:
Performance ratio  - 8.789647093542246
 
Performance ratio  - 9.177105482660881
 
Performance ratio  - 8.520964375227406
Średnio wydajność w przypadkach, w których nie znaleziono żadnych dopasowań, wzrosła o 8,8 razy! Zamień ciąg w Javie - 4
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION