JavaRush /Blog Java /Random-ES /Analicemos la clase StringUtils

Analicemos la clase StringUtils

Publicado en el grupo Random-ES
Hola a todos, mis queridos lectores. Intento escribir sobre lo que realmente me interesa y lo que me preocupa en este momento. Por eso, hoy habrá una lectura ligera que te será útil como referencia en el futuro: hablemos de StringUtils . Analicemos la clase StringUtils - 1Dio la casualidad de que en un momento pasé por alto la biblioteca Apache Commons Lang 3 . Esta es una biblioteca con clases auxiliares para trabajar con diferentes objetos. Esta es una colección de métodos útiles para trabajar con cadenas, colecciones, etc. En un proyecto actual, donde tuve que trabajar con más detalle con cadenas para traducir una lógica empresarial de hace 25 años (de COBOL a Java), resultó que no tenía un conocimiento suficientemente profundo de la clase StringUtils . Entonces tuve que crear todo yo mismo. ¿Lo que quiero decir? El hecho de que no es necesario que usted mismo escriba ciertas tareas que implican manipulación de cadenas, sino que utilice una solución ya preparada. ¿Qué hay de malo en escribirlo tú mismo? Al menos en el sentido de que se trata de más código que ya se escribió hace mucho tiempo. No menos urgente es la cuestión de probar el código escrito adicionalmente. Cuando utilizamos una biblioteca que ha demostrado ser buena, esperamos que ya haya sido probada y que no necesitemos escribir un montón de casos de prueba para probarla. Da la casualidad de que el conjunto de métodos para trabajar con una cadena en Java no es tan grande. Realmente no hay muchos métodos que sean útiles para el trabajo. Esta clase también se crea para proporcionar comprobaciones de NullPointerException. El esquema de nuestro artículo será el siguiente:
  1. ¿Como conectar?
  2. Ejemplos de mi trabajo: cómo, sin conocer una clase tan útil, creé mi muleta para bicicleta .
  3. Veamos otros métodos que me parecieron interesantes.
  4. Resumamos.
Todos los casos se agregarán a un repositorio separado en la organización de la comunidad Javarush en GitHub. Habrá ejemplos y pruebas separados para ellos.

0. Cómo conectarse

Aquellos que caminan de la mano conmigo ya están más o menos familiarizados con Git y Maven, por lo que confiaré en este conocimiento y no me repetiré. Para aquellos que se perdieron mis artículos anteriores o recién comenzaron a leer, aquí hay materiales sobre Maven y Git . Por supuesto, sin un sistema de compilación (Maven, Gredl), también puedes conectar todo manualmente, pero hoy en día esto es una locura y definitivamente no necesitas hacerlo así: es mejor aprender de inmediato a hacer todo correctamente. Por lo tanto, para trabajar con Maven, primero agregamos la dependencia adecuada:
<dependency>
   <groupId>org.apache.commons</groupId>
   <artifactId>commons-lang3</artifactId>
   <version>${apache.common.version}</version>
</dependency>
Donde ${apache.common.version} es la versión de esta biblioteca. A continuación, para importar en alguna clase, agregue importar:
import org.apache.commons.lang3.StringUtils;
Y listo, está todo en la bolsa))

1. Ejemplos de un proyecto real

  • método leftPad

El primer ejemplo en general parece tan estúpido ahora que es muy bueno que mis colegas conocieran StringUtils.leftPad y me lo dijeran. Cuál era la tarea: el código estaba construido de tal manera que era necesario transformar los datos si no llegaban del todo correctamente. Se esperaba que el campo de cadena constara sólo de números, es decir si su longitud es 3 y su valor es 1, entonces la entrada debe ser "001". Es decir, primero debes eliminar todos los espacios y luego cubrirlos con ceros. Más ejemplos para aclarar la esencia de la tarea: desde “12“ -> “012” desde “1“ -> “001” Y así sucesivamente. ¿Qué hice? Descrito esto en la clase LeftPadExample . Escribí un método que hará todo esto:
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();
}
Como base, tomé la idea de que simplemente podemos obtener la diferencia entre el valor original y el recortado y llenarlo con ceros delante. Para hacer esto utilicé IntStream para hacer la misma operación n veces. Y esto definitivamente necesita ser probado. Esto es lo que podría haber hecho si hubiera sabido de antemano sobre el método StringUtils.leftPad :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.leftPad(value.trim(), value.length(), "0");
}
Como puede ver, hay mucho menos código y también se utiliza una biblioteca confirmada por todos. Para este propósito, creé dos pruebas en la clase LeftPadExampleTest (generalmente cuando planean probar una clase, crean una clase con el mismo nombre + Prueba en el mismo paquete, solo que en src/test/java). Estas pruebas verifican un método para garantizar que transforme correctamente el valor y luego otro. Por supuesto, sería necesario escribir muchas más pruebas, pero las pruebas no son el tema principal en nuestro caso:
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);
   }

}
Puedo hacer algunos comentarios sobre las pruebas por ahora. Están escritos usando JUnit 5:
  1. Una prueba se tratará como prueba si tiene la anotación adecuada: @Test.
  2. Si es difícil describir el funcionamiento de la prueba en el nombre o la descripción es larga e incómoda de leer, puede agregar la anotación @DisplayName y convertirla en una descripción normal que será visible al ejecutar las pruebas.
  3. Al escribir pruebas, utilizo el enfoque BDD, en el que divido las pruebas en partes lógicas:
    1. //dado - bloque de configuración de datos antes de la prueba;
    2. //cuándo es el bloque donde se lanza la parte del código que estamos probando;
    3. //entonces es un bloque en el que se comprueban los resultados del bloque cuando.
Si los ejecuta, confirmarán que todo funciona como se esperaba.

  • método stripStart

Aquí necesitaba resolver un problema con una línea que podía tener espacios y comas al principio. Después de la transformación, no deberían haber tenido un nuevo significado. El planteamiento del problema es más claro que nunca. Algunos ejemplos reforzarán nuestra comprensión: “, , libros” -> “libros” “,,, libros” -> “libros” b , libros” -> “b , libros” Como en el caso de leftPad, agregué el Clase StrimStartExample , en la que tiene dos métodos. Uno, con su propia solución:
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);
}
Aquí la idea era encontrar el índice a partir del cual no hay más espacios ni comas. Si no estaban allí al principio, entonces el índice será cero. Y el segundo, con una solución a través de StringUtils :
public static String apacheCommonLeftPad(String value) {
   return StringUtils.stripStart(value, StringUtils.SPACE + COMMA);
}
Aquí pasamos el primer argumento información sobre con qué cadena estamos trabajando, y en el segundo pasamos una cadena que consta de caracteres que deben omitirse. Creamos la clase StripStartExampleTest de la misma forma :
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);
   }
}

  • método está vacío

Este método, por supuesto, es mucho más sencillo, pero eso no lo hace menos útil. Amplía las capacidades del método String.isEmpty() , que también agrega una verificación de nulo. ¿Para qué? Para evitar NullPointerException, es decir, para evitar llamar a métodos en una variable que es nula . Por tanto, para no escribir:
if(value != null && value.isEmpty()) {
   //doing something
}
Simplemente puedes hacer esto:
if(StringUtils.isEmpty(value)) {
   //doing something
}
La ventaja de este método es que queda inmediatamente claro dónde se utiliza qué método.

2. Análisis de otros métodos de la clase StringUtils

Ahora hablemos de aquellos métodos que, en mi opinión, también merecen atención. Hablando en general de StringUtils , vale la pena decir que proporciona métodos seguros para nulos análogos a los que se encuentran en la clase String (como es el caso del método isEmpty ). Repasémoslos:

  • método de comparación

Este método existe en String y generará una NullPointerException si, al comparar dos cadenas, una de ellas es nula. Para evitar comprobaciones desagradables en nuestro código, podemos usar el método StringUtils.compare(String str1, String str2) : devuelve un int como resultado de la comparación. ¿Qué significan estos valores? int = 0 si son iguales (o ambos son nulos). int <0, si str1 es menor que str2. int > 0, si str1 es mayor que str2. Además, si consulta su documentación, el Javadoc de este método presenta los siguientes escenarios:
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... métodos

Aquí los desarrolladores de la utilidad se lo pasaron genial. Cualquier método que desee está ahí. Decidí juntarlos:
  1. contiene es un método que comprueba si la cadena esperada está dentro de otra cadena. ¿Cómo es esto útil? Puede utilizar este método si necesita asegurarse de que haya una palabra determinada en el texto.

    Ejemplos:

    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

    Nuevamente, la seguridad NPE (Null Pointer Exception) está presente.

  2. containsAny es un método que comprueba si alguno de los caracteres presentes en la cadena está presente. También es útil: a menudo hay que hacer esto.

    Ejemplos de la documentación:

    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 es una extensión útil del método contiene . De hecho, para comprobar este caso sin este método, deberá pasar por varias opciones. Y así sólo se utilizará armoniosamente un método.

  4. Algunos ejemplos de los documentos:

    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. contiene Ninguno: a juzgar por el nombre, ya puede comprender lo que se está verificando. No debe haber líneas en el interior. Algo útil, sin duda. Búsqueda rápida de algunos caracteres no deseados;). En nuestro robot de Telegram filtraremos obscenidades y no ignoraremos estos divertidos métodos.

    Y ejemplos, ¿dónde estaríamos sin ellos?

    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

  • método defaultString

Una serie de métodos que ayudan a evitar agregar información adicional si la cadena es nula y necesita establecer algún valor predeterminado. Hay muchas opciones para todos los gustos. El principal de ellos es StringUtils.defaultString(final String str, final String defaultStr) - en caso de que str sea nulo, simplemente pasaremos el valor defaultStr . Ejemplos de la documentación:
StringUtils.defaultString(null, "NULL")  = "NULL"
StringUtils.defaultString("", "NULL")    = ""
StringUtils.defaultString("bat", "NULL") = "bat"
Es muy conveniente usarlo cuando creas una clase POJO con datos.

  • método eliminar el espacio en blanco

Este es un método interesante, aunque no existen muchas opciones para su aplicación. Al mismo tiempo, si surge tal caso, el método definitivamente será muy útil. Elimina todos los espacios de la cadena. Dondequiera que esté esta brecha, no habrá rastro de ella))) Ejemplos de los documentos:
StringUtils.deleteWhitespace(null)         = null
StringUtils.deleteWhitespace("")           = ""
StringUtils.deleteWhitespace("abc")        = "abc"
StringUtils.deleteWhitespace("   ab  c  ") = "abc"

  • termina con el método

Habla por si mismo. Este es un método muy útil: comprueba si la cadena termina con la cadena sugerida o no. Esto suele ser necesario. Por supuesto, puede emitir el cheque usted mismo, pero utilizar un método ya preparado es claramente más conveniente y mejor. Ejemplos:
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
Como puede ver, todo termina con una línea vacía))) Creo que este ejemplo (StringUtils.endsWith("ABCDEF", "") = true) es solo una ventaja, porque esto es absurdo) También hay un método que ignora el caso.

  • método igual

Un gran ejemplo de un método seguro para nulos que compara dos cadenas. Independientemente de lo que pongamos allí, la respuesta estará ahí y no tendrá errores. Ejemplos:
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
Por supuesto, también existe equalsIgnoreCase : todo se hace exactamente de la misma manera, solo que ignoramos el caso. ¿Vamos a ver?
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

  • es igual a cualquier método

Sigamos adelante y ampliemos el método igual . Digamos que en lugar de varias comprobaciones de igualdad, queremos realizar una. Para ello podemos pasar una cadena con la que se comparará un conjunto de cadenas, si alguna de ellas es igual a la propuesta será VERDADERA. Pasamos una cadena y una colección de cadenas para compararlas entre sí (la primera cadena con las cadenas de la colección). ¿Difícil? Aquí hay ejemplos de los documentos para ayudarlo a comprender lo que significa:
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
También existe equalsAnyIgnoreCase . Y ejemplos para ello:
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

Línea de fondo

Como resultado, nos vamos con el conocimiento de qué es StringUtils y qué métodos útiles tiene. Bueno, sabiendo que existen cosas tan útiles y que no es necesario vallar cada vez con muletas en lugares donde sería posible cerrar el problema con la ayuda de una solución ya preparada. En general, hemos analizado sólo una parte de los métodos. Si lo deseas, puedo continuar: hay muchos más y realmente merecen atención. Si tiene alguna idea sobre de qué otra manera podría presentarse esto, escríbame; siempre estoy abierto a nuevas ideas. La documentación de los métodos está muy bien escrita, se agregan ejemplos de prueba con resultados, lo que ayuda a comprender mejor el funcionamiento del método. Por lo tanto, no dudamos en leer la documentación: disipará sus dudas sobre la funcionalidad de la utilidad. Para adquirir nueva experiencia en codificación, le aconsejo que observe cómo se crean y escriben las clases de utilidad. Esto será útil en el futuro, ya que normalmente cada proyecto tiene sus propias clases de scrap y la experiencia de escribirlas será útil. Tradicionalmente, les sugiero que se suscriban a mi cuenta en Github . Para aquellos que no conocen mi proyecto con un bot de Telegram, aquí hay un enlace al primer artículo . Gracias a todos por leer. Agregué algunos enlaces útiles a continuación.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION