JavaRush /Java Blog /Random-IT /Analizziamo la classe StringUtils
Roman Beekeeper
Livello 35

Analizziamo la classe StringUtils

Pubblicato nel gruppo Random-IT
Ciao a tutti, miei cari lettori. Cerco di scrivere di ciò che mi interessa veramente e di ciò che mi preoccupa in questo momento. Oggi, quindi, ci sarà qualche lettura leggera che vi sarà utile come riferimento in futuro: parliamo di StringUtils . Analizziamo la classe StringUtils - 1È successo che una volta ho bypassato la libreria Apache Commons Lang 3 . Questa è una libreria con classi ausiliarie per lavorare con oggetti diversi. Questa è una raccolta di metodi utili per lavorare con stringhe, raccolte e così via. In un progetto attuale, in cui ho dovuto lavorare più in dettaglio con le stringhe per tradurre la logica aziendale di 25 anni (da COBOL a Java), si è scoperto che non avevo una conoscenza sufficientemente approfondita della classe StringUtils . Quindi ho dovuto creare tutto da solo. Ciò che intendo? Il fatto che non devi scrivere tu stesso determinate attività che comportano la manipolazione delle stringhe, ma utilizzare una soluzione già pronta. Cosa c'è di sbagliato nello scriverlo da solo? Almeno in quanto si tratta di altro codice già scritto molto tempo fa. Non meno urgente è la questione del test del codice scritto in aggiunta. Quando utilizziamo una libreria che si è dimostrata valida, ci aspettiamo che sia già stata testata e che non abbiamo bisogno di scrivere una serie di casi di test per testarla. Accade così che l'insieme di metodi per lavorare con una stringa in Java non sia così ampio. Non ci sono davvero molti metodi che potrebbero essere utili per il lavoro. Questa classe viene creata anche per fornire controlli per NullPointerException. Lo schema del nostro articolo sarà il seguente:
  1. Come connettere?
  2. Esempi dal mio lavoro: come, senza conoscere un corso così utile, ho creato la mia stampella da bicicletta .
  3. Diamo un'occhiata ad altri metodi che ho trovato interessanti.
  4. Riassumiamo.
Tutti i casi verranno aggiunti a un repository separato nell'organizzazione della community Javarush su GitHub. Ci saranno esempi e test separati per loro.

0. Come connettersi

Coloro che camminano mano nella mano con me hanno già più o meno familiarità sia con Git che con Maven, quindi farò affidamento su questa conoscenza e non mi ripeterò. Per coloro che si sono persi i miei articoli precedenti o hanno appena iniziato a leggere, ecco i materiali su Maven e Git . Certo, senza un sistema di build (Maven, Gredl), puoi anche collegare tutto manualmente, ma al giorno d'oggi è una follia e sicuramente non è necessario farlo così: è meglio imparare subito a fare tutto correttamente. Pertanto, per lavorare con Maven, aggiungiamo prima la dipendenza appropriata:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
Dove ${apache.common.version} è la versione di questa libreria. Successivamente, per importare in qualche classe, aggiungi import:
import org.apache.commons.lang3.StringUtils;
E questo è tutto, è tutto nella borsa))

1. Esempi da un progetto reale

  • metodo leftPad

Il primo esempio in generale sembra così stupido ora che è molto positivo che i miei colleghi sapessero di StringUtils.leftPad e me lo abbiano detto. Qual era il compito: il codice era costruito in modo tale che fosse necessario trasformare i dati se non arrivavano correttamente. Ci si aspettava che il campo stringa fosse composto solo da numeri, ad es. se la sua lunghezza è 3 e il suo valore è 1, la voce dovrebbe essere "001". Cioè, prima devi rimuovere tutti gli spazi e poi coprirli con zeri. Altri esempi per rendere chiara l'essenza del compito: da “12“ -> “012” da “1“ -> “001” E così via. Cosa ho fatto? Descritto questo nella classe LeftPadExample . Ho scritto un metodo che farà tutto questo:
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();
}
Come base, ho preso l'idea che possiamo semplicemente ottenere la differenza tra il valore originale e quello tagliato e riempirla con degli zeri davanti. Per fare questo ho usato IntStream per fare la stessa operazione n volte. E questo è sicuramente da testare. Ecco cosa avrei potuto fare se avessi saputo in anticipo del metodo StringUtils.leftPad :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
Come puoi vedere, c'è molto meno codice e viene utilizzata anche una libreria confermata da tutti. A questo scopo ho creato due test nella classe LeftPadExampleTest (di solito quando pensano di testare una classe, creano una classe con lo stesso nome + Test nello stesso pacchetto, solo in src/test/java). Questi test controllano un metodo per garantire che trasformi correttamente il valore, quindi un altro. Naturalmente, bisognerebbe scrivere molti più test, ma nel nostro caso i test non sono l’argomento principale:
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);
   }

}
Per ora posso fare alcuni commenti sui test. Sono scritti utilizzando JUnit 5:
  1. Un test verrà trattato come test se ha l'annotazione appropriata - @Test.
  2. Se risulta difficile descrivere il funzionamento del test nel nome o la descrizione è lunga e scomoda da leggere, è possibile aggiungere l'annotazione @DisplayName e renderla una normale descrizione che sarà visibile durante l'esecuzione dei test.
  3. Quando scrivo i test, utilizzo l'approccio BDD, in cui divido i test in parti logiche:
    1. //dato - blocco di impostazione dei dati prima del test;
    2. //quando è il blocco dove viene lanciata la parte di codice che stiamo testando;
    3. //then è un blocco in cui vengono controllati i risultati del blocco quando.
Se li esegui, confermeranno che tutto funziona come previsto.

  • metodo stripStart

Qui dovevo risolvere un problema con una riga che poteva contenere spazi e virgole all'inizio. Dopo la trasformazione, non avrebbero dovuto avere un nuovo significato. L’affermazione del problema è più chiara che mai. Alcuni esempi rafforzeranno la nostra comprensione: “, , libri” -> “libri” “,,, libri” -> “libri” b , libri” -> “b , libri” Come nel caso di leftPad, ho aggiunto il Classe StrimStartExample , in cui ha due metodi. Uno - con la propria soluzione:
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);
}
Qui l'idea era quella di trovare l'indice a partire dal quale non ci siano più spazi o virgole. Se non fossero presenti all'inizio, l'indice sarà pari a zero. E il secondo - con una soluzione tramite StringUtils :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Qui passiamo al primo argomento le informazioni su quale stringa stiamo lavorando, e nel secondo passiamo una stringa composta da caratteri che devono essere saltati. Creiamo la classe StripStartExampleTest allo stesso modo :
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);
   }
}

  • Metodo isEmpty

Questo metodo, ovviamente, è molto più semplice, ma ciò non lo rende meno utile. Estende le capacità del metodo String.isEmpty() , che aggiunge anche un controllo per null. Per quello? Per evitare NullPointerException, ovvero evitare di chiamare metodi su una variabile che è null . Pertanto, per non scrivere:
if(value != null && value.isEmpty()) {
   //doing something
}
Puoi semplicemente fare questo:
if(StringUtils.isEmpty(value)) {
   //doing something
}
Il vantaggio di questo metodo è che è immediatamente chiaro dove viene utilizzato quale metodo.

2. Analisi di altri metodi della classe StringUtils

Parliamo ora di quei metodi che, secondo me, meritano anche attenzione. Parlando in generale di StringUtils , vale la pena dire che fornisce metodi null sicuri analoghi a quelli presenti nella classe String (come nel caso del metodo isEmpty ). Esaminiamoli:

  • metodo di confronto

Tale metodo esiste in String e lancerà una NullPointerException se, confrontando due stringhe, una di esse è nulla. Per evitare brutti controlli nel nostro codice, possiamo utilizzare il metodo StringUtils.compare(String str1, String str2) : restituisce un int come risultato del confronto. Cosa significano questi valori? int = 0 se sono uguali (o entrambi sono nulli). int < 0, se str1 è minore di str2. int > 0, se str1 è maggiore di str2. Inoltre, se guardi la loro documentazione, il Javadoc di questo metodo presenta i seguenti scenari:
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

  • contiene... metodi

Qui gli sviluppatori dell'utilità si sono divertiti molto. Qualunque sia il metodo che desideri, c'è. Ho deciso di metterli insieme:
  1. contiene è un metodo che controlla se la stringa prevista è all'interno di un'altra stringa. In che modo è utile? Puoi utilizzare questo metodo se devi assicurarti che ci sia una determinata parola nel testo.

    Esempi:

    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

    Anche in questo caso è presente la sicurezza NPE (Null Pointer Exception).

  2. contieneAny è un metodo che controlla se sono presenti alcuni dei caratteri presenti nella stringa. Anche una cosa utile: devi farlo spesso.

    Esempi dalla documentazione:

    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. contieneIgnoreCase è un'estensione utile al metodo contiene . In effetti, per verificare un caso del genere senza questo metodo, dovrai passare attraverso diverse opzioni. E così verrà utilizzato armoniosamente un solo metodo.

  4. Alcuni esempi dai documenti:

    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. contieneNone : a giudicare dal nome, puoi già capire cosa viene controllato. Non dovrebbero esserci linee all'interno. Una cosa utile, sicuramente. Ricerca rapida di alcuni caratteri indesiderati ;). Nel nostro bot di Telegram filtreremo le oscenità e non ignoreremo questi metodi divertenti.

    Ed esempi, dove saremmo senza di loro:

    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

  • metodo defaultString

Una serie di metodi che aiutano a evitare di aggiungere informazioni aggiuntive se la stringa è nulla ed è necessario impostare un valore predefinito. Ci sono molte opzioni per soddisfare tutti i gusti. Il principale tra questi è StringUtils.defaultString(final String str, final String defaultStr) - nel caso in cui str sia nullo, passeremo semplicemente il valore defaultStr . Esempi dalla documentazione:
StringUtils.defaultString(null, "NULL")  = "NULL"
StringUtils.defaultString("", "NULL")    = ""
StringUtils.defaultString("bat", "NULL") = "bat"
È molto comodo da usare quando crei una classe POJO con dati.

  • metodo deleteWhitespace

Questo è un metodo interessante, sebbene non ci siano molte opzioni per la sua applicazione. Allo stesso tempo, se si presenta un caso del genere, il metodo sarà sicuramente molto utile. Rimuove tutti gli spazi dalla stringa. Ovunque sia questo divario, non ce ne sarà traccia))) Esempi dai documenti:
StringUtils.deleteWhitespace(null)         = null
StringUtils.deleteWhitespace("")           = ""
StringUtils.deleteWhitespace("abc")        = "abc"
StringUtils.deleteWhitespace("   ab  c  ") = "abc"

  • terminaCon metodo

Parla da solo. Questo è un metodo molto utile: controlla se la stringa termina con la stringa suggerita oppure no. Questo è spesso necessario. Naturalmente puoi scrivere tu stesso l'assegno, ma utilizzare un metodo già pronto è chiaramente più conveniente e migliore. Esempi:
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
Come puoi vedere, tutto termina con una riga vuota))) Penso che questo esempio (StringUtils.endsWith("ABCDEF", "") = true) sia solo un bonus, perché è assurdo) Esiste anche un metodo che ignora il caso.

  • metodo uguale

Un ottimo esempio di metodo null sicuro che confronta due stringhe. Qualunque cosa inseriamo, la risposta sarà lì e sarà senza errori. Esempi:
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
Naturalmente esiste anche equalsIgnoreCase : tutto viene fatto esattamente allo stesso modo, solo che ignoriamo il caso. Vediamo?
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

  • metodo equalsAny

Andiamo avanti ed estendiamo il metodo equals . Diciamo che invece di diversi controlli di uguaglianza, vogliamo eseguirne uno. Per questo possiamo passare una stringa con la quale verrà confrontato un insieme di stringhe; se qualcuna di esse è uguale a quella proposta, sarà TRUE. Passiamo una stringa e una raccolta di stringhe per confrontarle tra loro (la prima stringa con le stringhe della raccolta). Difficile? Ecco alcuni esempi tratti dai documenti per aiutarti a capire cosa si intende:
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
C'è anche equalsAnyIgnoreCase . Ed esempi per questo:
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

Linea di fondo

Di conseguenza, partiamo con la conoscenza di cos'è StringUtils e quali metodi utili ha. Ebbene, con la consapevolezza che esistono cose così utili e non è necessario recintare ogni volta con le stampelle in luoghi in cui sarebbe possibile chiudere la questione con l'aiuto di una soluzione già pronta. In generale, abbiamo analizzato solo una parte dei metodi. Se vuoi posso continuare: ce ne sono molti altri e meritano davvero attenzione. Se hai qualche idea su come altrimenti potrebbe essere presentato, scrivimi: sono sempre aperto a nuove idee. La documentazione dei metodi è scritta molto bene, vengono aggiunti esempi di test con risultati, il che aiuta a comprendere meglio il funzionamento del metodo. Pertanto, non esitiamo a leggere la documentazione: dissiperà i tuoi dubbi sulla funzionalità dell'utilità. Per acquisire nuova esperienza di codifica, ti consiglio di guardare come vengono create e scritte le classi di utilità. Ciò sarà utile in futuro, poiché di solito ogni progetto ha le proprie classi di scarto e l'esperienza di scriverle tornerà utile. Tradizionalmente consiglio di iscriverti al mio account su Github ) Per chi non conosce il mio progetto con un bot di Telegram, ecco il link al primo articolo . Grazie a tutti per aver letto. Ho aggiunto alcuni link utili qui sotto.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION