CodeGym /Blogue Java /Random-PT /Comparar comparações de String e Equals em Java
John Squirrels
Nível 41
San Francisco

Comparar comparações de String e Equals em Java

Publicado no grupo Random-PT
Oi! Hoje falaremos sobre um tópico muito importante e interessante, ou seja, comparar objetos com objetos (Compare Strings e Equals). Então, em Java, quando exatamente o objeto A seria igual ao objeto B ? Vamos tentar escrever um exemplo:

public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {
      
       Car car1 = new Car();
       car1.model = "Ferrari";
       car1.maxSpeed = 300;

       Car car2 = new Car();
       car2.model = "Ferrari";
       car2.maxSpeed = 300;

       System.out.println(car1 == car2);
   }
}
Saída do console: false Aguarde, pare. Por que esses dois carros não são iguais? Atribuímos a eles as mesmas propriedades, mas o resultado da comparação é falso. A resposta é simples. O operador == compara referências de objetos, não propriedades de objetos. Dois objetos podem até ter 500 campos com valores idênticos, mas compará-los ainda resultaria em falso. Afinal, as referências car1 e car2apontam para dois objetos diferentes, ou seja, para dois endereços diferentes. Imagine uma situação em que você está comparando pessoas. Certamente, em algum lugar do mundo existe uma pessoa que compartilha o mesmo nome, cor dos olhos, idade, altura, cor do cabelo, etc. Isso os torna semelhantes em muitos aspectos, mas vocês ainda não são gêmeos - e obviamente não são a mesma pessoa.
Igualdades e comparações de strings - 2
O operador == usa aproximadamente essa mesma lógica quando o usamos para comparar dois objetos. Mas e se você precisar que seu programa use uma lógica diferente? Por exemplo, suponha que seu programa execute uma análise de DNA. Ele compara o código genético de duas pessoas e determina se são gêmeos.

public class Man {

   int geneticCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.geneticCode = 1111222233;

       Man man2 = new Man();
       man2.geneticCode = 1111222233;

       System.out.println(man1 == man2);
   }
}
Saída do console: false Obtemos o mesmo resultado lógico (porque não mudamos muito), mas agora essa lógica não é boa! Afinal, na vida real, a análise de DNA deveria nos dar 100% de garantia de que temos gêmeos diante de nós. Mas nosso programa e o operador == nos dizem o contrário. Como mudamos esse comportamento e garantimos que o programa forneça o resultado correto quando o DNA corresponder? Java tem um método especial para isso: equals() . Assim como o método toString() , que discutimos anteriormente, equals() pertence à classe Object — a classe mais importante em Java, a classe da qual derivam todas as outras classes. Mas igual()não altera o comportamento do nosso programa por si só:

public class Man {

   String geneticCode;

   public static void main(String[] args) {

       Man man1 = new Man();
       man1.geneticCode = "111122223333";

       Man man2 = new Man();
       man2.geneticCode = "111122223333";

       System.out.println(man1.equals(man2));
   }
}
Saída do console: false Exatamente o mesmo resultado, então para que precisamos desse método? :/ É tudo simples. O problema aqui é que atualmente estamos usando esse método conforme ele é implementado na classe Object . E se entrarmos no código da classe Object e olharmos a implementação do método, veremos isso:

public boolean equals(Object obj) {
   return (this == obj);
}
Essa é a razão pela qual o comportamento do programa não mudou! O mesmo operador == (que compara referências) é usado dentro do método equals() da classe Object . Mas o truque com esse método é que podemos substituí-lo. Substituir significa escrever seu próprio método equals() em nossa classe Man , dando a ele o comportamento de que precisamos! No momento, não gostamos do fato de que man1.equals(man2) é essencialmente equivalente a man1 == man2 . Aqui está o que faremos nesta situação:

public class Man { 

   int dnaCode; 

   public boolean equals(Man man) { 
       return this.dnaCode ==  man.dnaCode; 

   } 

   public static void main(String[] args) { 

       Man man1 = new Man(); 
       man1.dnaCode = 1111222233; 

       Man man2 = new Man(); 
       man2.dnaCode = 1111222233; 

       System.out.println(man1.equals(man2)); 

   } 
} 
Saída do console: true Agora obtemos um resultado totalmente diferente! Ao escrever nosso próprio método equals() e usá-lo em vez do método padrão, produzimos o comportamento correto: agora, se duas pessoas tiverem o mesmo DNA, o programa relatará "A análise de DNA provou que são gêmeos" e retornará verdadeiro! Substituindo o método equals() em suas classes, você pode facilmente criar qualquer lógica de comparação de objetos que precisar. Na verdade, acabamos de tocar na comparação de objetos. À nossa frente, ainda há uma grande lição independente sobre esse tópico (você pode dar uma olhada agora, se estiver interessado).

Comparando strings em Java

Por que estamos considerando comparações de strings separadamente de todo o resto? A realidade é que as strings são um assunto por si só na programação. Primeiro, se você pegar todos os programas Java já escritos, descobrirá que cerca de 25% dos objetos neles são strings. Então esse tema é muito importante. Em segundo lugar, o processo de comparação de strings é realmente muito diferente de outros objetos. Considere um exemplo simples:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2);
   }
}
Saída do console: false Mas por que obtivemos false? Afinal, as strings são exatamente as mesmas, palavra por palavra :/ Você deve ter adivinhado o motivo: é porque o operador == compara referências ! Claramente, s1 e s2 têm endereços diferentes na memória. Se você pensou nisso, vamos retrabalhar nosso exemplo:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = "CodeGym is the best website for learning Java!";
       System.out.println(s1 == s2);
   }
}
Agora temos novamente duas referências, mas o resultado é exatamente o oposto: Saída do console: true Desamparadamente confuso? Vamos descobrir o que está acontecendo. O operador == realmente compara endereços de memória. Isso é sempre verdade e você não precisa duvidar disso. Isso significa que se s1 == s2 retornar true, essas duas strings terão o mesmo endereço. E de fato isso é verdade! É hora de apresentar a você uma área especial da memória para armazenar strings: o pool de strings
Igualdades e comparações de strings - 3
O pool de strings é uma área para armazenar todos os valores de strings que você cria em seu programa. Por que foi criado? Como dissemos antes, as strings representam uma grande porcentagem de todos os objetos. Qualquer programa grande cria muitas strings. O conjunto de strings foi criado para economizar memória: as strings são colocadas lá e, em seguida, as strings criadas referem-se à mesma área de memória - não há necessidade de alocar memória adicional a cada vez. Toda vez que você escreve String = "........" o programa verifica se existe uma string idêntica no pool de strings. Se houver, uma nova string não será criada. E a nova referência apontará para o mesmo endereço no pool de strings (onde a string idêntica está localizada). Então, quando escrevemos

String s1 = "CodeGym is the best website for learning Java!";
String s2 = "CodeGym is the best website for learning Java!";
s2 aponta para o mesmo lugar que s1 . A primeira instrução cria uma nova string no pool de strings. A segunda instrução simplesmente se refere à mesma área de memória que s1 . Você poderia fazer outras 500 strings idênticas e o resultado não mudaria. Espere um minuto. Se isso for verdade, por que esse exemplo não funcionou antes?

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2);
   }
}
Acho que sua intuição já lhe disse o motivo =) Tente adivinhar antes de ler mais. Você pode ver que essas duas strings foram declaradas de maneiras diferentes. Um com o novo operador e outro sem ele. Aqui está a razão. Quando o operador new é usado para criar um objeto, ele aloca de forma forçada uma nova área de memória para o objeto. E uma string criada usando new não acaba no pool de strings — ela se torna um objeto separado, mesmo que seu texto corresponda perfeitamente a uma string no pool de strings. Ou seja, se escrevermos o seguinte código:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = "CodeGym is the best website for learning Java!";
       String s3 = new String("CodeGym is the best website for learning Java!");
   }
}
Na memória, fica assim:
Igualdades e comparações de strings - 4
E toda vez que você cria um novo objeto usando new , uma nova área de memória é alocada, mesmo que o texto dentro da nova string seja o mesmo! Parece que descobrimos o operador == . Mas e quanto ao nosso novo conhecido, o método equals() ?

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1.equals(s2));
   }
}
Saída do console: verdadeiro Interessante. Temos certeza de que s1 e s2 apontam para diferentes áreas da memória. Mas o método equals() ainda nos diz que eles são iguais. Por que? Lembra que dissemos anteriormente que o método equals() pode ser substituído para comparar objetos da maneira que quisermos? Isso é exatamente o que eles fizeram com a classe String . Substitui o equals ()método. E ao invés de comparar referências, ele compara a sequência de caracteres nas strings. Se o texto for o mesmo, não importa como eles foram criados ou onde estão armazenados: seja no pool de strings ou em uma área separada da memória. O resultado da comparação será verdadeiro. A propósito, o Java permite que você execute comparações de strings que não diferenciam maiúsculas de minúsculas. Normalmente, se uma das strings tiver todas as letras maiúsculas, o resultado da comparação será falso:

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CODEGYM IS THE BEST WEBSITE FOR LEARNING JAVA!");
       System.out.println(s1.equals(s2));
   }
}
Saída do console: false Para comparações que não diferenciam maiúsculas de minúsculas, a classe String tem o método equalsIgnoreCase() . Você pode usá-lo se quiser apenas comparar a sequência de caracteres específicos em vez de maiúsculas e minúsculas. Por exemplo, isso pode ser útil ao comparar dois endereços:

public class Main {

   public static void main(String[] args) {

       String address1 = "2311 Broadway Street, San Francisco";
       String address2 = new String("2311 BROADWAY STREET, SAN FRANCISCO");
       System.out.println(address1.equalsIgnoreCase(address2));
   }
}
Neste caso, obviamente estamos falando do mesmo endereço, então faz sentido usar o método equalsIgnoreCase() .

O método String.intern()

A classe String tem mais um método complicado: intern() ; O método intern() trabalha diretamente com o pool de strings. Se você chamar o método intern() em alguma string:
  • Ele verifica se há uma string correspondente no pool de strings
  • Se houver, ele retorna a referência à string no pool
  • Caso contrário, ele adiciona a string ao pool de strings e retorna uma referência a ela.
Depois de usar o método intern() em uma referência de string obtida usando new , podemos usar o operador == para compará-lo com uma referência de string do pool de strings.

public class Main {

   public static void main(String[] args) {

       String s1 = "CodeGym is the best website for learning Java!";
       String s2 = new String("CodeGym is the best website for learning Java!");
       System.out.println(s1 == s2.intern());
   }
}
Saída do console: true Quando comparamos essas strings anteriormente sem intern() , o resultado foi false. Agora o método intern() verifica se a string "CodeGym é o melhor site para aprender Java!" está no pool de strings. Claro que é: nós o criamos com

String s1 = "CodeGym is the best website for learning Java!";
Verificamos se s1 e a referência retornada por s2.intern() apontam para a mesma área de memória. E claro, eles fazem :) Em resumo, memorize e aplique esta importante regra: SEMPRE use o método equals() para comparar strings! Ao comparar strings, quase sempre pretendemos comparar seus caracteres em vez de referências, áreas de memória ou qualquer outra coisa. O método equals() faz exatamente o que você precisa. Para reforçar o que você aprendeu, sugerimos que você assista a uma vídeo aula do nosso Curso de Java
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION