Strings no Java: Não Use o Operador de Igualdade!

Quando vamos comparar textos no Java (Strings), é inevitável pensarmos que isso é feito da mesma forma que comparar números. E nessa hora você certamente fica em tentado em fazer algo desse tipo:

 

String s1 = "a";
String s2 = "a";
    
if (s1 == s2) {
    // faça algo
} else {
    // faça outra coisa
}

 

Só que, por mais estranho que possa parecer, isso não funciona. Ou melhor, não funciona às vezes, o que é ainda pior…

Bom, mas deixa eu te explicar o que está por trás de tudo isso…

Dá só uma olhada neste código:

 

String s1 = "abc";
String s2 = "abc";

System.out.println(s1 == s2);

 

Ao executar o código acima, o resultado é true (verdadeiro), como é de se esperar. Afinal de contas, estamos pensando na comparação do texto “abc” com o texto “abc”, e eles são iguais…

Agora olha só o que acontece se a gente fizer uma pequena modificação:

 

String s1 = "abc";
String s2 = new String("abc");

System.out.println(s1 == s2);

 

Ao executar, você vai perceber que o resultado agora é false (falso).

Isso mesmo: aos olhos do Java, as strings “abc” e “abc” são diferentes.

Isso realmente parece não fazer sentido, mas agora eu vou te mostrar por que o Java tira essa conclusão.

 

Conheça o Pool de Strings do Java!

Em algumas linguagens de programação, o tipo String faz parte do conjunto de tipos primitivos da linguagem.

Mas no Java String é uma classe, o que significa que os objetos de String que são criados seguem a mesma regra de armazenamento em memória das outras classes do Java. Eles são armazenadas no heap e as variáveis do tipo String referenciam essas áreas de memória no heap onde ficam os objetos (não vou aprofundar essa questão aqui pra não perder o foco, então fica pra outro post).

Mas isso tem um efeito colateral

Como strings são MUITO usadas em qualquer tipo de aplicação, isso pode causar um DESPERDÍCIO ABSURDO de memória.

Imagine se o seu sistema usa a string “A” em 20 partes do código. Será que é preciso existir 20 objetos de String na memória pra guardar a mesma informação? Pois é, totalmente desnecessário…

Pensando nisso, os projetistas do Java tomaram uma decisão: criaram um mecanismo de compartilhamento de strings, que eles chamaram de pool de strings.

O pool é uma área de memória que armazena strings únicas. Quando você faz algo como isso aqui…

 

String s1 = "a";
String s2 = "a";
String s3 = "b";
String s4 = "b";
String s5 = "c";

 

… são criadas 3 strings no pool: “a”, “b” e “c”. E cada variável (s1, s2, s3, etc.) referencia um desses objetos que estão no pool.

Graficamente, seria algo como o desenho abaixo:

 

 

(Aliás, o fato de objetos String no pool serem compartilhados é o motivo pelo qual strings em Java são imutáveis. Mas isso é uma discussão pra outro momento…)

 

Quando o operador de igualdade funciona

O operador de igualdade do Java (==) compara o conteúdo de duas variáveis.

Quando usamos tipos primitivos (int, double, boolean, etc.) ele se comporta sempre como o esperado, pois nos tipos primitivos o que está dentro da variável é realmente o valor dela.

Mas no caso de classes (como a String), o conteúdo da variável é uma referência pra um objeto na memória (que está no heap).

Portanto, a conclusão que chegamos é que, no caso de strings, o operador de igualdade não compara o conteúdo das strings, mas sim as referências de memória (que é o que está efetivamente sendo armazenado pelas variáveis).

Ok, ok… Confesso que isso tudo pode ser meio confuso até você se acostumar, então vou dar um exemplo.

Vamos relembrar o primeiro código que eu te mostrei no post:

 

String s1 = "a";
String s2 = "a";

 

Quando você faz isso, o que está acontecendo na memória é  isso aqui:

 

 

Note que temos 1 único objeto String na memória (com o valor “a”) e 2 variáveis que referenciam o mesmo objeto.

Portanto, quando testamos s1 == s2, o resultado é true. Não porque os textos são iguais, por porque ambas as variáveis (s1 e s2) referenciam o mesmo objeto.

De novo: lembre que o == compara o conteúdo das variáveis, e o conteúdo aqui são referências pra objetos que estão no pool.

 

Quando o operador de igualdade NÃO funciona

Como eu já te mostrei antes, o == pode não se comportar como você espera. É o caso deste código aqui:

 

String s1 = "a";
String s2 = new String("a");

 

Quando esse código é executado, é assim que as coisas ficam na memória:

 

 

Note que s1 referencia a string “a” que está no pool; mas s2 referencia outro objeto String, com o conteúdo “a”, mas que está fora do pool.

Isto acontece porque a criação do objeto String, através do operador new(), faz com que seja criado um novo objeto em uma área de memória fora do pool.

Olhando o desenho, dá pra ver claramente que os conteúdos de s1 e s2 são diferentes (cada variável referencia um objeto diferente). Portanto a comparação s1 == s2 vai retornar, neste caso, false.

 

Afinal: Como Comparar Strings no Java?

Todo esse problema acontece pelo fato do == do Java comparar referências de memória ao invés de comparar o texto armazenado no objeto String.

Então a grande pergunta é: será que existe uma forma de comparar o texto da String, ao invés de comparar referências de memória?

E a resposta é SIM, EXISTE!

A partir de agora, TODAS as vezes que você precisar comparar strings no Java você vai usar o método equals().

O método equals() é definido na classe Object do Java. Como todas as classes do Java herdam diretamente ou indiretamente e de Object, significa que todas as classes do Java têm este método.

Na classe String, este método foi implementado de forma a comparar o texto, portanto ele resolve o nosso problema! Veja:

 

String s1 = "a";
String s2 = "a";

System.out.println(s1.equals(s2));

 

O resultado do código acima é true!

Agora olhe o código abaixo:

 

String s1 = "a";
String s2 = new String("a");

System.out.println(s1.equals(s2));

 

A resposta aqui também é true!

O fato do equals() comparar os textos (a comparação é feita de “a” com “a”), faz com que o resultado seja verdadeiro sempre, independentemente de como estão os objetos na memória.

 

“Mas Carlos, se eu não usar o new String() não vou ter problemas e posso usar == pra comparar strings. Estou certo?”

 

Não: você está ERRADO!

Mesmo que você não use o new String(), em muitos casos você não sabe como a String foi criada.

É muito comum chamarmos métodos de outras bibliotecas/frameworks/APIs ou até códigos que foram criados por outros desenvolvedores.

Então não pense duas vezes: use o equals() em todas as situações! 🙂

 

A aí, gostou desta sacada?

Eu dei uma Masterclass gratuita e super inspiradora, que foi recorde de audiência na Softblue com um convidado especial, revelando o passo a passo sobre como entrar no mercado de Java sem ter experiência e sem ter diploma.

Recomendo fortemente que você assista neste link. Depois me conte o que você achou! 😀

Sobre o autor

Carlos Tosin

Carlos Tosin

Carlos Eduardo Gusso Tosin é formado em Ciência da Computação pela PUC-PR, pós-graduado em Desenvolvimento de Jogos para Computador pela Universidade Positivo, Mestre em Informática na área de Sistemas Distribuídos, também pela PUC-PR. Trabalha profissionalmente com Java desde 2002 e possui 6 anos de experiência no desenvolvimento de sistemas para a IBM dos Estados Unidos, utilizados a nível mundial. Atua desde 2005 com treinamentos de profissionais em grandes empresas e escreveu diversos artigos para a revista Java Magazine. Possui as certificações da Sun (agora Oracle) SCJP, SCJD,SCWCD, SCBCD, SCEA, IBM SOA e ITIL Foundation.

Comentários (3)

  • Tadeu Espíndola Palermo

    Excelente post Carlos, comparar Strings utilizando o operador de igualdade é algo comum entre os “calouros” do Java! Sem sombra de dúvidas esse assunto foi abordado de forma muito esclarecedora! Parabéns pelo rico conteúdo!!!

    • Carlos Tosin
      Carlos Tosin

      Esse é um problema que acontece bastante e gera muitas dúvidas.

  • Handersonn

    Opa, bela dica!! Vai ajudar bastante.

Deixe uma resposta para Carlos Tosin Cancelar resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *