JavaRush /Java-Blog /Random-DE /Lassen Sie uns die StringUtils-Klasse aufschlüsseln

Lassen Sie uns die StringUtils-Klasse aufschlüsseln

Veröffentlicht in der Gruppe Random-DE
Hallo zusammen, meine lieben Leser. Ich versuche darüber zu schreiben, was mich wirklich interessiert und was mir im Moment Sorgen bereitet. Daher gibt es heute eine kurze Lektüre, die Ihnen in Zukunft als Referenz nützlich sein wird: Lassen Sie uns über StringUtils sprechen . Lassen Sie uns die StringUtils-Klasse aufschlüsseln – 1Es ist einfach so passiert, dass ich einmal die Apache Commons Lang 3 -Bibliothek umgangen habe . Dies ist eine Bibliothek mit Hilfsklassen zum Arbeiten mit verschiedenen Objekten. Dies ist eine Sammlung nützlicher Methoden zum Arbeiten mit Zeichenfolgen, Sammlungen usw. Bei einem aktuellen Projekt, bei dem ich bei der Übersetzung einer 25 Jahre alten Geschäftslogik (von COBOL nach Java) detaillierter mit Strings arbeiten musste, stellte sich heraus, dass ich nicht über ausreichende Kenntnisse der StringUtils-Klasse verfügte . Also musste ich alles selbst erstellen. Was ich meine? Die Tatsache, dass Sie bestimmte Aufgaben zur String-Manipulation nicht selbst schreiben müssen, sondern eine vorgefertigte Lösung verwenden. Was ist falsch daran, es selbst zu schreiben? Zumindest insofern handelt es sich hierbei um Code, der bereits vor langer Zeit geschrieben wurde. Nicht weniger dringlich ist die Frage, den zusätzlich geschriebenen Code zu testen. Wenn wir eine Bibliothek verwenden, die sich als gut erwiesen hat, gehen wir davon aus, dass sie bereits getestet wurde und wir nicht viele Testfälle schreiben müssen, um sie zu testen. Es ist einfach so, dass der Satz an Methoden zum Arbeiten mit einem String in Java nicht so groß ist. Es gibt wirklich nicht viele Methoden, die für die Arbeit nützlich wären. Diese Klasse wird auch erstellt, um Prüfungen für NullPointerException bereitzustellen. Der Grundriss unseres Artikels wird wie folgt lauten:
  1. Wie verbinde ich mich?
  2. Beispiele aus meiner Arbeit: Wie ich, ohne von einer so nützlichen Klasse zu wissen, meine Fahrradkrücke hergestellt habe .
  3. Schauen wir uns andere Methoden an, die ich interessant fand.
  4. Fassen wir zusammen.
Alle Fälle werden einem separaten Repository in der Javarush-Community-Organisation auf GitHub hinzugefügt. Für sie wird es separate Beispiele und Tests geben.

0. So verbinden Sie sich

Diejenigen, die mit mir Hand in Hand gehen, sind sowohl mit Git als auch mit Maven bereits mehr oder weniger vertraut, daher werde ich mich weiterhin auf dieses Wissen verlassen und mich nicht wiederholen. Für diejenigen, die meine vorherigen Artikel verpasst haben oder gerade mit dem Lesen begonnen haben, finden Sie hier Materialien zu Maven und Git . Natürlich kann man ohne Build-System (Maven, Gredl) auch alles manuell verbinden, aber das ist heutzutage verrückt und das muss man auf keinen Fall so machen: Man lernt lieber gleich, wie man alles richtig macht. Um mit Maven arbeiten zu können, fügen wir daher zunächst die entsprechende Abhängigkeit hinzu:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
Wobei ${apache.common.version} die Version dieser Bibliothek ist. Um als Nächstes in eine Klasse zu importieren, fügen Sie import hinzu:
import org.apache.commons.lang3.StringUtils;
Und das ist alles, es ist alles in der Tasche))

1. Beispiele aus einem realen Projekt

  • leftPad-Methode

Das erste Beispiel erscheint mir jetzt im Allgemeinen so dumm, dass es sehr gut ist, dass meine Kollegen von StringUtils.leftPad wussten und es mir erzählten. Was war die Aufgabe: Der Code war so aufgebaut, dass eine Transformation der Daten erforderlich war, wenn diese nicht ganz korrekt ankamen. Es wurde erwartet, dass das String-Feld nur aus Zahlen bestehen sollte, d. h. Wenn seine Länge 3 und sein Wert 1 beträgt, sollte der Eintrag „001“ lauten. Das heißt, Sie müssen zuerst alle Leerzeichen entfernen und sie dann mit Nullen überdecken. Weitere Beispiele, um das Wesentliche der Aufgabe zu verdeutlichen: von „12“ -> „012“ von „1“ -> „001“ Und so weiter. Was habe ich getan? Beschrieben wurde dies in der LeftPadExample- Klasse . Ich habe eine Methode geschrieben, die das alles erledigt:
public static String ownLeftPad(String value) {
   String trimmedValue = value.trim();

   if(trimmedValue.length() == value.length()) {
       return value;
   }

   StringBuilder newValue = new StringBuilder(trimmedValue);

   IntStream.rangeClosed(1, value.length() - trimmedValue.length())
           .forEach(it -> newValue.insert(0, "0"));
   return newValue.toString();
}
Als Grundlage habe ich die Idee genommen, dass wir einfach die Differenz zwischen dem Original- und dem beschnittenen Wert ermitteln und mit vorangestellten Nullen auffüllen können. Dazu habe ich IntStream verwendet , um den gleichen Vorgang n-mal auszuführen. Und das muss unbedingt getestet werden. Folgendes hätte ich tun können, wenn ich im Voraus über die StringUtils.leftPad -Methode Bescheid gewusst hätte :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
Wie Sie sehen, gibt es viel weniger Code und es wird auch eine von allen bestätigte Bibliothek verwendet. Zu diesem Zweck habe ich zwei Tests in der Klasse LeftPadExampleTest erstellt (normalerweise erstellen sie, wenn sie eine Klasse testen möchten, eine Klasse mit demselben Namen + Test im selben Paket, nur in src/test/java). Diese Tests überprüfen eine Methode, um sicherzustellen, dass sie den Wert korrekt umwandelt, und dann eine andere. Natürlich müssten noch viel mehr Tests geschrieben werden, aber Testen ist in unserem Fall nicht das Hauptthema:
package com.github.javarushcommunity.stringutilsdemo;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

@DisplayName("Unit-level testing for LeftPadExample")
class LeftPadExampleTest {

   @DisplayName("Should transform by using ownLeftPad method as expected")
   @Test
   public void shouldTransformOwnLeftPadAsExpected() {
       //given
       String value = "1   ";
       String expectedTransformedValue = "0001";

       //when
       String transformedValue = LeftPadExample.ownLeftPad(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }

   @DisplayName("Should transform by using StringUtils method as expected")
   @Test
   public void shouldTransformStringUtilsLeftPadAsExpected() {
       //given
       String value = "1   ";
       String expectedTransformedValue = "0001";

       //when
       String transformedValue = LeftPadExample.apacheCommonLeftPad(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }

}
Zu den Tests kann ich vorerst noch ein paar Anmerkungen machen. Sie sind mit JUnit 5 geschrieben:
  1. Ein Test wird als Test behandelt, wenn er über die entsprechende Annotation verfügt – @Test.
  2. Wenn es schwierig ist, die Funktionsweise des Tests im Namen zu beschreiben, oder die Beschreibung lang und unpraktisch zu lesen ist, können Sie die Annotation @DisplayName hinzufügen und daraus eine normale Beschreibung machen, die beim Ausführen von Tests sichtbar ist.
  3. Beim Schreiben von Tests verwende ich den BDD-Ansatz, bei dem ich die Tests in logische Teile unterteile:
    1. //gegeben – Daten-Setup-Block vor dem Test;
    2. //wann ist der Block, in dem der Teil des Codes, den wir testen, gestartet wird;
    3. //then ist ein Block, in dem die Ergebnisse des when-Blocks überprüft werden.
Wenn Sie sie ausführen, bestätigen sie, dass alles wie erwartet funktioniert.

  • stripStart-Methode

Hier musste ich ein Problem mit einer Zeile lösen, die am Anfang Leerzeichen und Kommas enthalten konnte. Nach der Transformation hätten sie keine neue Bedeutung haben dürfen. Die Problemstellung ist klarer denn je. Ein paar Beispiele werden unser Verständnis verstärken: „, , Bücher“ -> „Bücher“ „,,, Bücher“ -> „Bücher“ b , Bücher“ -> „b , Bücher“ Wie im Fall von leftPad habe ich das hinzugefügt StrimStartExample- Klasse , die über zwei Methoden verfügt. Eins - mit eigener Lösung:
public static String ownStripStart(String value) {
   int index = 0;
   List commaSpace = asList(" ", ",");
   for (int i = 0; i < value.length(); i++) {
       if (commaSpace.contains(String.valueOf(value.charAt(i)))) {
           index++;
       } else {
           break;
       }
   }
   return value.substring(index);
}
Hier ging es darum, den Index zu finden, ab dem keine Leerzeichen oder Kommas mehr stehen. Wenn sie zu Beginn überhaupt nicht vorhanden waren, ist der Index Null. Und das zweite - mit einer Lösung über StringUtils :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Hier übergeben wir als erstes Argument Informationen darüber, mit welcher Zeichenfolge wir arbeiten, und als zweites übergeben wir eine Zeichenfolge bestehend aus Zeichen, die übersprungen werden müssen. Wir erstellen die Klasse StripStartExampleTest auf die gleiche Weise :
package com.github.javarushcommunity.stringutilsdemo;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("Unit-level testing for StripStartExample")
class StripStartExampleTest {

   @DisplayName("Should transform by using stripStart method as expected")
   @Test
   public void shouldTransformOwnStripStartAsExpected() {
       //given
       String value = ", , books";
       String expectedTransformedValue = "books";

       //when
       String transformedValue = StripStartExample.ownStripStart(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }

   @DisplayName("Should transform by using StringUtils method as expected")
   @Test
   public void shouldTransformStringUtilsStripStartAsExpected() {
       //given
       String value = ", , books";
       String expectedTransformedValue = "books";

       //when
       String transformedValue = StripStartExample.apacheCommonLeftPad(value);

       //then
       Assertions.assertEquals(expectedTransformedValue, transformedValue);
   }
}

  • isEmpty-Methode

Diese Methode ist natürlich viel einfacher, aber das macht sie nicht weniger nützlich. Es erweitert die Funktionen der String.isEmpty() -Methode , die auch eine Prüfung auf Null hinzufügt. Wofür? Um NullPointerException zu vermeiden, d. h. um zu vermeiden, dass Methoden für eine Variable aufgerufen werden, die null ist . Um also nicht zu schreiben:
if(value != null && value.isEmpty()) {
   //doing something
}
Sie können dies einfach tun:
if(StringUtils.isEmpty(value)) {
   //doing something
}
Der Vorteil dieser Methode besteht darin, dass sofort klar ist, wo welche Methode eingesetzt wird.

2. Analyse anderer Methoden der StringUtils-Klasse

Lassen Sie uns nun über die Methoden sprechen, die meiner Meinung nach auch Aufmerksamkeit verdienen. Wenn man allgemein über StringUtils spricht , ist es erwähnenswert, dass es nullsichere Methoden bereitstellt, die denen der String- Klasse ähneln (wie es bei der isEmpty- Methode der Fall ist ). Gehen wir sie durch:

  • Vergleichsmethode

Eine solche Methode existiert in String und löst eine NullPointerException aus, wenn beim Vergleich zweier Strings einer davon null ist. Um hässliche Prüfungen in unserem Code zu vermeiden, können wir die Methode StringUtils.compare(String str1, String str2) verwenden : Sie gibt als Ergebnis des Vergleichs ein int zurück. Was bedeuten diese Werte? int = 0, wenn sie gleich sind (oder beide null sind). int < 0, wenn str1 kleiner als str2 ist. int > 0, wenn str1 größer als str2 ist. Wenn Sie sich außerdem die Dokumentation ansehen, stellt das Javadoc dieser Methode die folgenden Szenarios dar:
StringUtils.compare(null, null)   = 0
StringUtils.compare(null , "a")   < 0
StringUtils.compare("a", null)    > 0
StringUtils.compare("abc", "abc") = 0
StringUtils.compare("a", "b")     < 0
StringUtils.compare("b", "a")     > 0
StringUtils.compare("a", "B")     > 0
StringUtils.compare("ab", "abc")  < 0

  • enthält... Methoden

Hier hatten die Utility-Entwickler viel Spaß. Welche Methode Sie auch immer wünschen, es gibt sie. Ich habe beschlossen, sie zusammenzustellen:
  1. enthält ist eine Methode, die prüft, ob sich die erwartete Zeichenfolge in einer anderen Zeichenfolge befindet. Wie ist das nützlich? Sie können diese Methode verwenden, wenn Sie sicherstellen müssen, dass der Text ein bestimmtes Wort enthält.

    Beispiele:

    StringUtils.contains(null, *)     = false
    StringUtils.contains(*, null)     = false
    StringUtils.contains("", "")      = true
    StringUtils.contains("abc", "")   = true
    StringUtils.contains("abc", "a")  = true
    StringUtils.contains("abc", "z")  = false

    Auch hier ist NPE-Sicherheit (Null Pointer Exception) vorhanden.

  2. enthältAny ist eine Methode, die prüft, ob eines der in der Zeichenfolge vorhandenen Zeichen vorhanden ist. Auch eine nützliche Sache: Das muss man oft tun.

    Beispiele aus der Dokumentation:

    StringUtils.containsAny(null, *)                  = false
    StringUtils.containsAny("", *)                    = false
    StringUtils.containsAny(*, null)                  = false
    StringUtils.containsAny(*, [])                    = false
    StringUtils.containsAny("zzabyycdxx", ['z', 'a']) = true
    StringUtils.containsAny("zzabyycdxx", ['b', 'y']) = true
    StringUtils.containsAny("zzabyycdxx", ['z', 'y']) = true
    StringUtils.containsAny("aba", ['z'])             = false

  3. enthältIgnoreCase ist eine nützliche Erweiterung der enthält- Methode . Um einen solchen Fall ohne diese Methode zu überprüfen, müssen Sie tatsächlich mehrere Optionen durchgehen. Daher wird nur eine Methode harmonisch eingesetzt.

  4. Ein paar Beispiele aus den Dokumenten:

    StringUtils.containsIgnoreCase(null, *) = false
    StringUtils.containsIgnoreCase(*, null) = false
    StringUtils.containsIgnoreCase("", "") = true
    StringUtils.containsIgnoreCase("abc", "") = true
    StringUtils.containsIgnoreCase("abc", "a") = true
    StringUtils.containsIgnoreCase("abc", "z") = false
    StringUtils.containsIgnoreCase("abc", "A") = true
    StringUtils.containsIgnoreCase("abc", "Z") = false

  5. enthältNone - anhand des Namens können Sie bereits verstehen, was überprüft wird. Es sollten keine Linien darin sein. Eine nützliche Sache, auf jeden Fall. Schnelle Suche nach unerwünschten Zeichen ;). In unserem Telegram-Bot filtern wir Obszönitäten und ignorieren diese lustigen Methoden nicht.

    Und Beispiele, was wären wir ohne sie:

    StringUtils.containsNone(null, *)       = true
    StringUtils.containsNone(*, null)       = true
    StringUtils.containsNone("", *)         = true
    StringUtils.containsNone("ab", '')      = true
    StringUtils.containsNone("abab", 'xyz') = true
    StringUtils.containsNone("ab1", 'xyz')  = true
    StringUtils.containsNone("abz", 'xyz')  = false

  • defaultString-Methode

Eine Reihe von Methoden, die das Hinzufügen zusätzlicher Informationen vermeiden, wenn die Zeichenfolge null ist und Sie einen Standardwert festlegen müssen. Es gibt viele Möglichkeiten für jeden Geschmack. Der wichtigste davon ist StringUtils.defaultString(final String str, final String defaultStr) – falls str null ist, übergeben wir den Wert einfach an defaultStr . Beispiele aus der Dokumentation:
StringUtils.defaultString(null, "NULL")  = "NULL"
StringUtils.defaultString("", "NULL")    = ""
StringUtils.defaultString("bat", "NULL") = "bat"
Es ist sehr praktisch, wenn Sie eine POJO-Klasse mit Daten erstellen.

  • deleteWhitespace-Methode

Dies ist eine interessante Methode, obwohl es nicht viele Möglichkeiten für ihre Anwendung gibt. Gleichzeitig wird die Methode in einem solchen Fall auf jeden Fall sehr nützlich sein. Es entfernt alle Leerzeichen aus der Zeichenfolge. Wo auch immer diese Lücke ist, es wird keine Spur davon geben))) Beispiele aus den Dokumenten:
StringUtils.deleteWhitespace(null)         = null
StringUtils.deleteWhitespace("")           = ""
StringUtils.deleteWhitespace("abc")        = "abc"
StringUtils.deleteWhitespace("   ab  c  ") = "abc"

  • endetWith-Methode

Spricht für sich selbst. Dies ist eine sehr nützliche Methode: Sie prüft, ob die Zeichenfolge mit der vorgeschlagenen Zeichenfolge endet oder nicht. Dies ist oft notwendig. Natürlich können Sie den Scheck auch selbst ausstellen, aber die Verwendung einer vorgefertigten Methode ist eindeutig bequemer und besser. Beispiele:
StringUtils.endsWith(null, null)      = true
StringUtils.endsWith(null, "def")     = false
StringUtils.endsWith("abcdef", null)  = false
StringUtils.endsWith("abcdef", "def") = true
StringUtils.endsWith("ABCDEF", "def") = false
StringUtils.endsWith("ABCDEF", "cde") = false
StringUtils.endsWith("ABCDEF", "")    = true
Wie Sie sehen, endet alles mit einer leeren Zeile))) Ich denke, dass dieses Beispiel (StringUtils.endsWith("ABCDEF", "") = true) nur ein Bonus ist, weil das absurd ist. Es gibt auch eine Methode, die ignoriert die Groß-/Kleinschreibung.

  • ist gleich Methode

Ein großartiges Beispiel für eine nullsichere Methode, die zwei Zeichenfolgen vergleicht. Was auch immer wir dort eingeben, die Antwort wird dort sein, und sie wird fehlerfrei sein. Beispiele:
StringUtils.equals(null, null)   = true
StringUtils.equals(null, "abc")  = false
StringUtils.equals("abc", null)  = false
StringUtils.equals("abc", "abc") = true
StringUtils.equals("abc", "ABC") = false
Natürlich gibt es auch equalIgnoreCase – alles wird genauso gemacht, nur dass wir den Fall ignorieren. Mal sehen?
StringUtils.equalsIgnoreCase(null, null)   = true
StringUtils.equalsIgnoreCase(null, "abc")  = false
StringUtils.equalsIgnoreCase("abc", null)  = false
StringUtils.equalsIgnoreCase("abc", "abc") = true
StringUtils.equalsIgnoreCase("abc", "ABC") = true

  • ist gleichJede Methode

Lassen Sie uns fortfahren und die Methode „equals“ erweitern . Nehmen wir an, wir möchten statt mehrerer Gleichheitsprüfungen nur eine durchführen. Dazu können wir eine Zeichenfolge übergeben, mit der eine Reihe von Zeichenfolgen verglichen werden. Wenn eine davon mit der vorgeschlagenen Zeichenfolge übereinstimmt, ist sie TRUE. Wir übergeben einen String und eine Sammlung von Strings, um sie miteinander zu vergleichen (den ersten String mit den Strings aus der Sammlung). Schwierig? Hier sind Beispiele aus den Dokumenten, die Ihnen helfen sollen, zu verstehen, was gemeint ist:
StringUtils.equalsAny(null, (CharSequence[]) null) = false
StringUtils.equalsAny(null, null, null)    = true
StringUtils.equalsAny(null, "abc", "def")  = false
StringUtils.equalsAny("abc", null, "def")  = false
StringUtils.equalsAny("abc", "abc", "def") = true
StringUtils.equalsAny("abc", "ABC", "DEF") = false
Es gibt auch equalAnyIgnoreCase . Und Beispiele dafür:
StringUtils.equalsAnyIgnoreCase(null, (CharSequence[]) null) = false
StringUtils.equalsAnyIgnoreCase(null, null, null)    = true
StringUtils.equalsAnyIgnoreCase(null, "abc", "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", null, "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", "abc", "def") = true
StringUtils.equalsAnyIgnoreCase("abc", "ABC", "DEF") = true

Endeffekt

Als Ergebnis wissen wir, was StringUtils ist und welche nützlichen Methoden es hat. Nun, mit der Erkenntnis, dass es so nützliche Dinge gibt und es nicht nötig ist, jedes Mal mit Krücken an Stellen zu zäunen, an denen es möglich wäre, das Problem mit Hilfe einer vorgefertigten Lösung zu lösen. Im Allgemeinen haben wir nur einen Teil der Methoden analysiert. Wenn Sie möchten, kann ich fortfahren: Es gibt noch viel mehr davon und sie verdienen wirklich Aufmerksamkeit. Wenn Sie Ideen haben, wie dies sonst noch präsentiert werden könnte, schreiben Sie mir bitte – ich bin immer offen für neue Ideen. Die Dokumentation zu den Methoden ist sehr gut geschrieben, Testbeispiele mit Ergebnissen werden hinzugefügt, was hilft, die Funktionsweise der Methode besser zu verstehen. Deshalb scheuen wir uns nicht, die Dokumentation zu lesen: Sie wird Ihre Zweifel an der Funktionalität des Dienstprogramms zerstreuen. Um neue Codierungserfahrungen zu sammeln, empfehle ich Ihnen, einen Blick darauf zu werfen, wie Utility-Klassen erstellt und geschrieben werden. Dies wird in Zukunft nützlich sein, da normalerweise jedes Projekt seine eigenen Scrap-Klassen hat und die Erfahrung beim Schreiben dieser Klassen von Nutzen sein wird. Traditionell schlage ich vor, dass Sie mein Konto auf Github abonnieren .) Für diejenigen, die nichts über mein Projekt mit einem Telegram-Bot wissen, ist hier ein Link zum ersten Artikel . Vielen Dank an alle fürs Lesen. Ich habe unten einige nützliche Links hinzugefügt.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION