Como comparar uma variável com um caracter em java

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:

Como comparar uma variável com um caracter em java

(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:

Como comparar uma variável com um caracter em java

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:

Como comparar uma variável com um caracter em java

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! 😀

Recentemente num projeto Android, eu cai na armadilha de comparar duas Strings com operador == e por não testar o código (nunca repitam isso crianças), o tiro acabou saindo pela culatra. Por esse motivo resolvi escrever esse artigo para explicar como comparar Strings em Java da forma correta. Então bora lá…

Essa foi a linha de código que me deu dor de barriga:

if (anoBat == "0") { ... }

É uma simples condição onde comparo um variável (do tipo String) com um uma outra String literal. Simples né? Num primeiro momento talvez você ache que retorne TRUE correto? Porém não foi isso que aconteceu… 😭

Vamos as explicações!

Uma coisa que precisa ficar bem claro: String em Java é um objeto!

O Java utiliza um mecanismo chamado String Interning. Ele armazena num pool de memória apenas uma cópia de cada valor distinto de String (que deve ser imutável).

Esse conjunto de caracteres literais formam uma instancia de String. Logo é possível comprar duas VARIÁVEIS do tipo String com o operador ==

String a = "str"; String b = "str"; System.out.printLn(a == b); // retorna TRUE

Porém não é inteligente confiar nesse operador de comparação uma vez que não sabemos como a String foi instanciada, por exemplo:

String a = "str"; String b = "string"; String c = b.substring(0,3); // c:"str" System.out.printLn(a == c); // retorna FALSE

Mas porque retornou FALSE? 🤔

No código acima foi criada uma NOVA instância de String, que não é a mesma retornada pela JVM para o literal “str”. (Lembre-se, cada instância de objeto tem um identificador único)

Apesar de existir duas instâncias distintas, lá no pool de memória continua existindo apenas uma entrada (imutável) para “str”.

Tá certo, mas como validar isso? Existe o método String.intern() que retorna uma referência para a String que está no pool. Exemplo:

String a = "str"; String b = "string"; String c = b.substring(0,3); // c:"str" System.out.printLn(a == c.intern()); // retorna TRUE

Legal né? Mas nem tudo são flores…

Mas e o tal do equals()?

Em resumo o método equals() simplesmente compara a estrutura entre dois objetos (em nosso caso os caracteres literais), sem a necessidade de atribuir novos valores (ou ter acesso) ao pool.

A comparação com == é mais rápida que utilizando o método equals(), porém não é sábio (nem recomendado) usar intern(). Saca só:

Nem todas as Strings são armazenadas no pool imediatamente. Quando chamamos o método intern(), caso a String ainda não exista, o Java vai acrescenta-la ao pool. O PROBLEMA é que uma vez no pool essa String vai para a memória permanente e não será mais coletada pelo garbage collector.

Se você usar incansavelmente o intern(), em processamentos pouco mais pesados como arquivos de texto, XML, banco de dados, é bem provável que você possa ter um estouro de pilha (OutOfMemoryError).

Então o que usar?

SEMPRE que for comparar duas Strings use o método equals(). Simples assim. Você evita muita dor de cabeça desnecessária. 😁

String a = "str"; System.out.printLn(a.equals("str")); // retorna TRUE

E aí curtiu? Conseguiu clarear as idéias? Deixe sua opinião logo abaixo, me fala se já passou por algo parecido e como foi combinado? 👊