No trabalho de um programador, muitas vezes algumas tarefas ou seus componentes podem ser repetidos. Portanto, hoje gostaria de abordar um tema que é frequentemente encontrado no trabalho diário de qualquer desenvolvedor Java. Vamos supor que você receba uma determinada string de um determinado método. E tudo parece bom, mas tem alguma coisinha que não combina com você. Por exemplo, o separador não é adequado e você precisa de outro (ou não precisa). O que pode ser feito em tal situação? Naturalmente, use os métodos
replace
da classe String
.
Substituição de string Java
O tipo objetoString
possui quatro variações do método de substituição replace
:
replace(char, char);
replace(CharSequence, CharSequence);
replaceFirst(String, String);
replaceAll(String, String).
replace(char, char)
String replace(char oldChar, char newChar)
- substitui todas as ocorrências do caractere do primeiro argumento oldChar
pelo segundo - newChar
. Neste exemplo, substituiremos a vírgula por ponto e vírgula:
String value = "In JavaRush, Diego the best, Diego is Java God".replace(',', ';');
System.out.println(value);
Saída do console:
In JavaRush; Diego the best; Diego is Java God
2.replace(CharSequence, CharSequence)
Substitui cada subsequência de uma sequência que corresponde a uma sequência especificada de caracteres por uma sequência de caracteres de substituição.
String value = "In JavaRush, Diego the best, Diego is Java God".replace("Java", "Rush");
System.out.println(value);
Conclusão:
In RushRush, Diego the best, Diego is Rush God
3.replaceFirst(String, String)
String replaceFirst(String regex, String replacement)
- Substitui a primeira substring que corresponde à expressão regular especificada por uma string de substituição. Ao usar uma expressão regular inválida, você pode capturar uma PatternSyntaxException (o que não é bom). Neste exemplo, vamos substituir o nome do robô campeão:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceFirst("Diego", "Amigo");
System.out.println(value);
Saída do console:
In JavaRush, Amigo the best, Diego is Java God
Como podemos ver, apenas a primeira ocorrência de “Diego” mudou, mas as subsequentes permaneceram de fora – ou seja, intocadas. 4. replaceAll()
em Java String replaceAll(String regex, String replacement)
- este método substitui todas as ocorrências de uma substring em uma string regex
por replacement
. Uma expressão regular pode ser usada como primeiro argumento regex
. Como exemplo, vamos tentar realizar a substituição anterior por nomes, mas com um novo método:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("Diego", "Amigo");
System.out.println(value);
Saída do console:
In JavaRush, Amigo the best, Amigo is Java God
Como podemos ver, todos os símbolos foram totalmente substituídos pelos necessários. Acho que o Amigo vai ficar satisfeito =)
Expressões regulares
Foi dito acima que é possível substituir usando uma expressão regular. Primeiro, vamos esclarecer o que é uma expressão regular? Expressões regulares são uma linguagem formal para busca e manipulação de substrings em texto, baseada no uso de metacaracteres (curingas). Simplificando, é um padrão de caracteres e metacaracteres que define uma regra de pesquisa. Por exemplo:\D
- um modelo que descreva qualquer carácter não digital; \d
— define qualquer caractere numérico, que também pode ser descrito como [0-9]
; [a-zA-Z]
— um modelo que descreve caracteres latinos de a a z, sem distinção entre maiúsculas e minúsculas; Considere a aplicação em um método replaceAll
de classe String
:
String value = "In JavaRush, Diego the best, Diego is Java God".replaceAll("\\s[a-zA-Z]{5}\\s", " Amigo ");
System.out.println(value);
Saída do console:
In JavaRush, Amigo the best, Amigo is Java God
\\s[a-zA-Z]{5}\\s
— descreve uma palavra de 5 caracteres latinos cercados por espaços. Conseqüentemente, este modelo é substituído pela string que passamos.
Substituição de regex Java
Basicamente, para usar expressões regulares em Java, os recursos dojava.util.regex
. As principais aulas são:
Pattern
- uma classe que fornece uma versão compilada de uma expressão regular.Matcher
— esta classe interpreta o padrão e determina correspondências na string que recebe.
Matcher
e 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);
E nossa conclusão será a mesma:
In JavaRush, Amigo the best, Amigo is Java God
Você pode ler mais sobre expressões regulares neste artigo .
Alternativa para substituir tudo
Não há dúvida de que os métodosreplace
impressionam String
muito, mas não se pode ignorar o fato de que String
se trata de immutable
um objeto, ou seja, não pode ser alterado após sua criação. Portanto, quando substituímos algumas partes de uma string usando métodos replace
, não alteramos o objeto String
, mas criamos um novo a cada vez, com o conteúdo necessário. Mas criar um novo objeto a cada vez leva muito tempo, não é? Especialmente quando a questão não é alguns objetos, mas algumas centenas, ou mesmo milhares. Quer queira quer não, você começa a pensar em alternativas. E que alternativas temos? Hmm... Quando se trata de String
sua propriedade immutable
, você imediatamente pensa em alternativas, mas não immutable
, ou seja, StringBuilder/StringBuffer . Como lembramos, na verdade essas classes não diferem, exceto que StringBuffer
são otimizadas para uso em um ambiente multithread, portanto, StringBuilder
funcionam um pouco mais rápido no uso de thread único. Com base nisso, hoje usaremos StringBuilder.
Esta classe tem muitos métodos interessantes, mas especificamente agora estamos interessados em replace
. StringBuilder replace(int start, int end, String str)
— este método substitui caracteres em uma substring desta sequência por caracteres na string especificada. A substring começa no início especificado e continua até o caractere no final do índice -1
ou até o final da sequência, se tal caractere não existir. Vejamos um exemplo:
StringBuilder strBuilder = new StringBuilder("Java Rush");
strBuilder.replace(5, 9, "God");
System.out.println(strBuilder);
Conclusão:
Java God
Como você pode ver, indicamos o intervalo em que queremos escrever a string, e escrevemos uma substring em cima do que está no intervalo. Então, usando a ajuda, StringBuilder
recriaremos um análogo do método replaceall java
. Como será:
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();
}
É assustador à primeira vista, mas com um pouco de compreensão você pode entender que nem tudo é tão complicado e é bastante lógico.Temos três argumentos:
str
— uma linha na qual queremos substituir algumas substrings;oldStr
— representação de substrings que iremos substituir;newStr
- com o que iremos substituí-lo.
if
Precisamos do primeiro para verificar os dados recebidos e, se a string str
estiver oldStr
vazia ou a nova substring newStr
for igual à antiga oldStr
, a execução do método não terá sentido. Portanto, retornamos a string original - str
. newStr
Em seguida, verificamos null
, e se for esse o caso, então o convertemos em um formato de string vazio que é mais conveniente para nós - ""
. Depois temos a declaração das variáveis que precisamos:
- comprimento total da string
str
; - comprimento da substring
oldStr
; - objeto
StringBuilder
de uma string compartilhada.
StringBuilder
- indexOf
- descobrimos o índice da primeira ocorrência da substring em que estamos interessados. Infelizmente, gostaria de ressaltar que ele indexOf
não funciona com expressões regulares, portanto nosso método final só funcionará com ocorrências de strings (( Se esse índice for igual a -1
, então não há mais ocorrências dessas ocorrências no objeto atual StringBuilder
, então saímos do método com o resultado de interesse: ele está contido em nosso StringBuilder
, para o qual convertemos String
, usando toString
. Se nosso índice for igual -1
na primeira iteração do loop, então a substring que precisa ser substituída não estava no geral string inicialmente. Portanto, em tal situação, simplesmente retornamos a string geral. Em seguida temos e o método descrito acima é usado replace
para StringBuilder
usar o índice encontrado da ocorrência para indicar as coordenadas da substring a ser substituída. Este loop será executado quantas vezes forem encontradas substrings que precisam ser substituídas. Se a string consistir apenas no caractere que precisa ser substituído, então somente neste caso teremos O loop será executado completamente e obteremos o resultado StringBuilder
convertido em uma string. Precisamos verificar a exatidão desse método, certo? Vamos escrever um teste que verifica o funcionamento do método em diversas situações:
@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, "");
}
Pode ser dividido em 7 testes separados, cada um dos quais será responsável pelo seu próprio caso de teste. Depois de lançado, veremos que é verde, ou seja, um sucesso. Bem, isso parece ser tudo. Embora espere, dissemos acima que este método será muito mais rápido replaceAll
que o String
. Bem, vamos dar uma olhada:
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 seguir, esse código foi executado três vezes e obtivemos os seguintes resultados: Saída do console:
Performance ratio - 5.012148941181627
Performance ratio - 5.320637176017641
Performance ratio - 4.719192686500394
Como podemos ver, em média nosso método é 5 vezes mais produtivo que a replaceAll
aula clássica! String
Bem, finalmente, vamos fazer a mesma verificação, mas, por assim dizer, em vão. Em outras palavras, no caso em que nenhuma correspondência for encontrada. Vamos substituir a string de pesquisa de "tyu"
para "--"
. Três execuções produziram os seguintes resultados: Saída do console:
Performance ratio - 8.789647093542246
Performance ratio - 9.177105482660881
Performance ratio - 8.520964375227406
Em média, o desempenho nos casos em que nenhuma correspondência foi encontrada aumentou 8,8 vezes!
GO TO FULL VERSION