En el trabajo de un programador, con bastante frecuencia algunas tareas o sus componentes pueden repetirse. Por eso, hoy me gustaría tocar un tema que se encuentra a menudo en el trabajo diario de cualquier desarrollador de Java. Supongamos que recibe una determinada cadena de un determinado método. Y todo parece estar bien, pero hay alguna pequeña cosa que no te conviene. Por ejemplo, el separador no es adecuado y necesita otro (o ninguno). ¿Qué se puede hacer en tal situación? Naturalmente, utilice los métodos
replace
de la clase String
.
Reemplazo de cadena de Java
El tipo objetoString
tiene cuatro variaciones del método de reemplazo replace
:
replace(char, char);
replace(CharSequence, CharSequence);
replaceFirst(String, String);
replaceAll(String, String).
replace(char, char)
String replace(char oldChar, char newChar)
- reemplaza todas las apariciones del carácter del primer argumento oldChar
con el segundo - newChar
. En este ejemplo, reemplazaremos la coma con un punto y coma:
String value = "In JavaRush, Diego the best, Diego is Java God".replace(',', ';');
System.out.println(value);
Salida de consola:
In JavaRush; Diego the best; Diego is Java God
2.replace(CharSequence, CharSequence)
Reemplaza cada subcadena de una cadena que coincide con una secuencia de caracteres específica con una secuencia de caracteres de reemplazo.
String value = "In JavaRush, Diego the best, Diego is Java God".replace("Java", "Rush");
System.out.println(value);
Conclusión:
In RushRush, Diego the best, Diego is Rush God
3.replaceFirst(String, String)
String replaceFirst(String regex, String replacement)
- Reemplaza la primera subcadena que coincide con la expresión regular especificada con una cadena de reemplazo. Cuando se utiliza una expresión regular no válida, se puede detectar una excepción PatternSyntaxException (lo cual no es bueno). En este ejemplo, reemplacemos el nombre del robot campeón:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceFirst("Diego", "Amigo");
System.out.println(value);
Salida de consola:
In JavaRush, Amigo the best, Diego is Java God
Como podemos ver, sólo la primera entrada de “Diego” ha cambiado, pero las siguientes quedaron omitidas, es decir, intactas. 4. replaceAll()
en Java String replaceAll(String regex, String replacement)
: este método reemplaza todas las apariciones de una subcadena en una cadena regex
con replacement
. Se puede utilizar una expresión regular como primer argumento regex
. Como ejemplo, intentemos realizar el reemplazo anterior con nombres, pero con un nuevo método:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("Diego", "Amigo");
System.out.println(value);
Salida de consola:
In JavaRush, Amigo the best, Amigo is Java God
Como podemos ver, todos los símbolos han sido reemplazados completamente por los necesarios. Creo que Amigo estará contento =)
Expresiones regulares
Se dijo anteriormente que es posible reemplazar usando una expresión regular. Primero, aclaremos qué es una expresión regular. Las expresiones regulares son un lenguaje formal para buscar y manipular subcadenas en el texto, basado en el uso de metacaracteres (comodines). En pocas palabras, es un patrón de caracteres y metacaracteres que define una regla de búsqueda. Por ejemplo:\D
- una plantilla que describa cualquier carácter no digital; \d
— define cualquier carácter numérico, que también puede describirse como [0-9]
; [a-zA-Z]
— una plantilla que describe los caracteres latinos de la a a la z, sin distinguir entre mayúsculas y minúsculas; Considere la aplicación en un método replaceAll
de clase String
:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("\\s[a-zA-Z]{5}\\s", " Amigo ");
System.out.println(value);
Salida de consola:
In JavaRush, Amigo the best, Amigo is Java God
\\s[a-zA-Z]{5}\\s
— describe una palabra de 5 caracteres latinos rodeados de espacios. En consecuencia, esta plantilla se reemplaza con la cadena que pasamos.
Reemplazo de expresiones regulares de Java
Básicamente, para utilizar expresiones regulares en Java, las capacidades dejava.util.regex
. Las clases clave son:
Pattern
- una clase que proporciona una versión compilada de una expresión regular.Matcher
— esta clase interpreta el patrón y determina coincidencias en la cadena que recibe.
Matcher
y 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);
Y nuestra conclusión será la misma:
In JavaRush, Amigo the best, Amigo is Java God
Puede leer más sobre expresiones regulares en este artículo .
Alternativa para reemplazar todo
No hay duda de que los métodosreplace
son String
muy impresionantes, pero no se puede ignorar el hecho de que String
es immutable
un objeto, es decir, no se puede cambiar después de su creación. Por lo tanto, cuando reemplazamos algunas partes de una cadena usando métodos replace
, no cambiamos el objeto String
, sino que creamos uno nuevo cada vez, con el contenido necesario. Pero crear un objeto nuevo cada vez lleva mucho tiempo, ¿no es así? Especialmente cuando la cuestión no es un par de objetos, sino un par de cientos, o incluso miles. Quieras o no, empiezas a pensar en alternativas. ¿Y qué alternativas tenemos? Hmm... Cuando se trata de String
su propiedad immutable
, inmediatamente piensas en alternativas, pero no immutable
, a saber, StringBuilder/StringBuffer . Como recordamos, estas clases en realidad no difieren, excepto que StringBuffer
están optimizadas para su uso en un entorno de subprocesos múltiples, por lo que StringBuilder
funcionan algo más rápido en el uso de un solo subproceso. En base a esto, hoy usaremos StringBuilder.
Esta clase tiene muchos métodos interesantes, pero ahora nos interesa específicamente replace
. StringBuilder replace(int start, int end, String str)
— este método reemplaza caracteres en una subcadena de esta secuencia con caracteres en la cadena especificada. La subcadena comienza al principio especificado y continúa hasta el carácter al final del índice, -1
o hasta el final de la secuencia si dicho carácter no existe. Veamos un ejemplo:
StringBuilder strBuilder = new StringBuilder("Java Rush");
strBuilder.replace(5, 9, "God");
System.out.println(strBuilder);
Conclusión:
Java God
Como puedes ver, indicamos el intervalo en el que queremos escribir la cadena, y escribimos una subcadena encima de lo que hay en el intervalo. Entonces, usando la ayuda, StringBuilder
recrearemos un análogo del método replaceall java
. Cómo se verá:
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();
}
Da miedo a primera vista, pero con un poco de comprensión se puede entender que no todo es tan complicado y es bastante lógico. Tenemos tres argumentos:
str
— una línea en la que queremos reemplazar algunas subcadenas;oldStr
— representación de subcadenas que reemplazaremos;newStr
- con qué lo reemplazaremos.
if
Necesitamos el primero para verificar los datos entrantes, y si la cadena str
está oldStr
vacía o la nueva subcadena newStr
es igual a la anterior oldStr
, entonces ejecutar el método no tendrá sentido. Por lo tanto, devolvemos la cadena original - str
. A continuación, verificamos newStr
y null
, si este es el caso, lo convertimos a un formato de cadena vacía que sea más conveniente para nosotros: ""
. Luego tenemos la declaración de las variables que necesitamos:
- longitud total de la cuerda
str
; - longitud de la subcadena
oldStr
; - objeto
StringBuilder
de una cadena compartida.
StringBuilder
- indexOf
- encontramos el índice de la primera aparición de la subcadena que nos interesa. Desafortunadamente, me gustaría señalar que indexOf
no funciona con expresiones regulares, por lo que nuestro método final solo funcionará con apariciones de cadenas (( Si este índice es igual a -1
, entonces no hay más apariciones de estas ocurrencias en el objeto actual StringBuilder
, entonces salimos del método con el resultado de interés: está contenido en nuestro StringBuilder
, al que convertimos String
usando toString
. Si nuestro índice es igual -1
en la primera iteración del bucle, entonces la subcadena que necesita ser reemplazada no estaba en el general cadena inicialmente. Por lo tanto, en tal situación, simplemente devolvemos la cadena general. A continuación tenemos y el método descrito anteriormente se utiliza replace
para StringBuilder
usar el índice encontrado de la ocurrencia para indicar las coordenadas de la subcadena a reemplazar. Este bucle se ejecutará tantas veces como se encuentren subcadenas que necesitan ser reemplazadas. Si la cadena consta solo del carácter que necesita ser reemplazado, entonces solo en este caso tenemos El bucle se ejecutará por completo y obtendremos el resultado StringBuilder
convertido en una cadena. Necesitamos comprobar la exactitud de este método, ¿verdad? Escribamos una prueba que verifique el funcionamiento del método en diversas situaciones:
@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, "");
}
Se puede dividir en 7 pruebas independientes, cada una de las cuales será responsable de su propio caso de prueba. Al lanzarlo veremos que está en color verde, es decir, exitoso. Bueno, eso parece ser todo. Aunque espera, dijimos anteriormente que este método será mucho más rápido replaceAll
que String
. Bueno, echemos un vistazo:
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);
A continuación, este código se ejecutó tres veces y obtuvimos los siguientes resultados: Salida de la consola:
Performance ratio - 5.012148941181627
Performance ratio - 5.320637176017641
Performance ratio - 4.719192686500394
Como podemos ver, en promedio nuestro método es 5 veces más productivo que la replaceAll
clase clásica. String
Bueno, finalmente, hagamos la misma verificación, pero, por así decirlo, en vano. En otras palabras, en el caso de que no se encuentre ninguna coincidencia. Reemplacemos la cadena de búsqueda de "tyu"
a "--"
. Tres ejecuciones arrojaron los siguientes resultados: Salida de la consola:
Performance ratio - 8.789647093542246
Performance ratio - 9.177105482660881
Performance ratio - 8.520964375227406
En promedio, el rendimiento en los casos en los que no se encontraron coincidencias aumentó 8,8 veces.
GO TO FULL VERSION