English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Análise detalhada de problemas de comparação em Java

O problema de comparação em Java é um problema muito básico e também muito confuso. Hoje, faremos uma comparação detalhada e organizada sobre alguns pontos que são fáceis de cometer erros, esperando que ajude o aprendizado e a entrevista de todos.

Um, a diferença entre == e equals()

Primeiro, precisamos saber a diferença entre == e equals(). O símbolo == sempre compara valores de endereço, para os tipos de dados básicos, a comparação com == é realmente se os valores das variáveis são iguais, enquanto para os tipos de dados de referência, compara-se os valores de endereço. Aqui é especialmente importante notar o tipo String, que é fácil de usar == acidentalmente e cometer erros. O método equals() é um método da classe Object, sabemos que em Java, todas as classes herdam implicitamente a classe Object, então todos os objetos de classe terão o método equals(). O método equals() da classe Object é mostrado na figura a seguir:

Pode ser visto pelo código-fonte que o método equals() da classe Object também usa == na camada inferior, então o que ele realmente compara são os valores de endereço. Portanto, se quisermos usar o método equals() para fazer outras comparações, precisamos reescrever o método equals().

Dois, tipos de dados básicos e suas classes de pacote

Todos nós sabemos que byte, short, int, long, boolean, char, double, float esses oito são tipos de dados básicos, as variáveis declaradas neles são armazenadas na memória da pilha. Enquanto os tipos de pacote correspondentes (Byte, Short, Integer, Long, Boolean, Character, Double) definem variáveis que existem na memória do heap. Para os tipos de dados básicos, a comparação é relativamente simples, ou seja, para verificar se são iguais usamos ==, e para comparar tamanhos usamos <, >, <=, >=. Para os tipos de pacote, porém, é um pouco diferente.

Primeiro, para verificar a igualdade, veja o resultado da execução do seguinte código:

package dailytest;
import org.junit.Test;
/**
 * Resumo de comparação em Java
 * @author yrr
 */
public class JavaCompareTest {
  /**
   * Verificação de igualdade do tipo Integer
   */
  @Test
  public void test01() {
    int n3 = 48;
    System.out.println("--------Ao usar objetos new, quando o valor está entre-127,128entre] quando---------");
    Integer n7 = new Integer(48);
    Integer n8 = new Integer(48);
    System.out.println(n7 == n8);  //false
    System.out.println(n7 == n3);  //true
    System.out.println("--------Atribuição direta, quando o valor está entre-128,127entre] quando---------");
    Integer n1 = 48;
    Integer n2 = 48;
    System.out.println(n3 == n1); //true
    System.out.println(n1 == n2); //true
    System.out.println(n1.equals(n2)); //true
    System.out.println(n1.equals(n3)); //true
    System.out.println(n1.intValue() == n2.intValue()); //true
    System.out.println("--------Atribuição direta, quando o valor não está entre-127,128entre] quando---------");
    Integer n4 = 128;
    Integer n5 = 128;
    int n6 = 128;
    System.out.println(n4 == n5);  //false
    System.out.println(n4 == n6);  //true
    System.out.println(n4.equals(n5)); //true
    System.out.println(n4.equals(n6)); //true
    System.out.println(n4.intValue() == n5.intValue()); //true
    //Ao usar o método Integer.intValue(), é necessário verificar se é null para evitar a ocorrência de NullPointException
  }
  /**
   * Verificação de igualdade do tipo Long
   */
  @Test
  public void test02() {
    //Aqui, é importante notar que, ao usar long para definição, não é necessário adicionar L ou l, enquanto ao usar Long, é obrigatório adicioná-lo, senão será gerado um erro
    //Os nomes dos projetos devem ser adicionados, para mostrar a diferença
    long n3 = 48L;
    System.out.println("--------Ao usar objetos new, quando o valor está entre-127,128entre] quando---------");
    Long n7 = new Long(48);
    Long n8 = new Long(48);
    System.out.println(n7 == n8);  //false
    System.out.println(n7 == n3);  //true
    System.out.println("--------Atribuição direta, quando o valor está entre-127,128entre] quando---------");
    Long n1 = 48L;
    Long n2 = 48L;
    System.out.println(n3 == n1); //true
    System.out.println(n1 == n2); //true
    System.out.println(n1.equals(n2)); //true
    System.out.println(n1.equals(n3)); //true
    System.out.println(n1.intValue() == n2.intValue()); //true
    System.out.println("--------Atribuição direta, quando o valor não está entre-127,128entre] quando---------");
    Long n4 = 128L;
    Long n5 = 128L;
    long n6 = 128;
    System.out.println(n4 == n5);  //false
    System.out.println(n4 == n6);  //true
    System.out.println(n4.equals(n5)); //true
    System.out.println(n4.equals(n6)); //true
    System.out.println(n4.intValue() == n5.intValue()); //true
    //Ao usar o método Long.intValue(), é necessário verificar se é null para evitar a ocorrência de NullPointException  
  }
}

Para o resultado de execução mencionado acima, faça a seguinte explicação:

Primeiro, para declarar um objeto Integer ou Long usando o método new, pois todos os novos objetos são alocados em um espaço na pilha, então, mesmo que os valores sejam iguais, para o ==, compara-se os valores de endereço, resultando em false. Para os tipos de dados primitivos de embalagem, o método equals() é reescrito, comparando o tamanho dos valores, então é possível usar o método equals() para fazer a comparação com base no tamanho do valor. No caso da comparação entre uma variável Integer e uma variável int, perceberemos que a comparação é feita com base no tamanho do valor, pois, no momento da comparação, o tipo Integer faz a desempacotagem automática, convertendo-se para o tipo int. A explicação dos três pontos anteriores se aplica a todos os tipos de embalagem Para o método de atribuição direta, o valor48dois Integer variáveis, o uso de == resulta em true, enquanto o valor128após isso, é false. Isso ocorre porque, no nível inferior, para os dois Integer n1 = 48; Este método de atribuição direta, na verdade, chama o método Integer.value(). Podemos simplesmente olhar para o código-fonte do método Integer.value(), como mostrado na figura a seguir:

Podemos ver que aqui há uma condição if, quando o valor de i está entre [-128,127], é retornado diretamente do array IntegerCache. Portanto, para os valores dentro desse intervalo, todos retornam o endereço correspondente do array. Portanto, o uso de == retornará true. Para aqueles fora desse intervalo, são novos objetos, então retornarão false. Este resultado é válido para os tipos Byte, Short, Integer, Long (aqueles interessados podem verificar o código-fonte dos métodos value() correspondentes), pois o intervalo do tipo Byte é [}}-128,127], então para o tipo Byte, usar == e equals() não há diferença. 

Para comparações de tamanho, é possível usar >, <, <=, >=, eles farão a desempacotagem automática. No entanto, geralmente recomendamos usar os seguintes dois métodos para comparações de tamanho:

Use o método xxxValue() para converter para tipo de dados básico e comparar. Use o método compareTo() para comparar. Em classes de pacote, o método compareTo() é sobrescrito. Verifique o código-fonte do compareTo(), você verá que ele usa a desempacotagem automática para converter para o tipo de dados básico correspondente e comparar.

II. Comparação de objetos Java

Com a introdução acima, a comparação de objetos torna-se mais fácil. A lógica é a mesma.

1. Comparação de tipos String

É necessário notar que o tipo String não pode usar >, <=, >=, < diretamente, o que causará uma exceção de compilação.

package dailytest;
import org.junit.Test;
/**
 * Resumo de comparação em Java
 * @author yrr
 */
public class JavaCompareTest {
  @Test
  public void test03() {
    String s1 = new String("123");
    String s2 = new String("123");
    System.out.println(s1 == s2);  //false
    System.out.println(s1.equals(s2));
    String s3 = "234";
    String s4 = "234";
    System.out.println(s3 == s4);  //true
    System.out.println(s3.equals(s4));  //true
    //System.out.println(s1 <= s3); //O operador < não está definido para o tipo de argumento(s) java.lang.String, java.lang.String
    System.out.println(s1.compareTo(s3) < 0);  //true
  }
}

 2. Comparação de objetos de classe

A conclusão da comparação de objetos de classe é a mesma, mas em comparação com tipos de dados básicos e String, é um pouco mais complexa.

Para comparar dois objetos com base em uma regra específica, é necessário sobrescrever o método equals() na classe a ser avaliada, exemplo de código a seguir:

package dailytest;
import org.junit.Test;
/**
 * Resumo de comparação em Java
 * @author yrr
 */
public class JavaCompareTest {
  @Test
  public void test04() {
    Person p1 = new Person("yrr",18);
    Person p2 = new Person("yrr",18);
    System.out.println(p1 == p2);  //false
    System.out.println(p2.equals(p1)); //true
  }
}
class Person{
  private String name;  
  private Integer age;
  public Person() {
  }
  public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
  }
  public String getName() {
    return name;
  }
  public Integer getAge() {
    return age;
  }
  @Override
  public boolean equals(Object obj) {
    Person person = (Person) obj;
    return name.equals(person.getName()) && age.equals(person.getAge());
  }
}

E se precisar comparar o tamanho de dois objetos (isto também é uma pergunta de entrevista comum), há duas maneiras:

A classe a ser comparada implementa a interface Comparable e reimplementa o método compareTo() Definiu uma classe que implementa a interface Comparator ou usou uma classe interna para reimplementar o método compare() As diferenças entre os dois: o primeiro é definido na classe a ser comparada, enquanto o segundo é definido fora da classe a ser comparada. Por meio dessa diferença, as vantagens e desvantagens dos dois são muito claras, o primeiro é simples, mas requer modificar a classe a ser comparada, enquanto o segundo não requer modificar o código original, o que é mais flexível.

A primeira forma, veja o exemplo de código a seguir:

package dailytest;
import org.junit.Test;
/**
 * Resumo de comparação em Java
 * @author yrr
 */
public class JavaCompareTest {
  @Test
  public void test5() {
    Person p1 = new Person("yrr",18);
    Person p2 = new Person("wx",19);
    System.out.println(p1.compareTo(p2) < 0);
  }
}
class Person implements Comparable<Person>{
  private String name;  
  private Integer age;
  public Person() {
  }
  public Person(String name, Integer age) {
    this.name = name;
    this.age = age;
  }
  public Integer getAge() {
    return age;
  }
  @Override
  public int compareTo(Person o) {
    return this.getAge(); - o.getAge();
  }  
}

A segunda forma, veja o exemplo de código a seguir:

package comparator;
import java.util.Arrays;
import java.util.Comparator;
public class MyComparator {
  public static void main(String[] args) {
    User[] users = new User[] { new User("u")}1001" 25),}} 
        new User("u1002" 20), new User("u1003" 21) };
    Arrays.sort(users, new Comparator<User>() {
      @Override
      public int compare(User o1, User o2) {
        return o1.getAge() - o2.getAge();
      }
    });
    for (int i = 0; i < users.length; i++) { 
      User user = users[i]; 
      System.out.println(user.getId() + " " + user.getAge()); 
    } 
  }
}
class User { 
  private String id; 
  private int age; 
  public User(String id, int age) { 
    this.id = id; 
    this.age = age; 
  } 
  public int getAge() { 
    return age; 
  } 
  public void setAge(int age) { 
    this.age = age; 
  } 
  public String getId() { 
    return id; 
  } 
  public void setId(String id) { 
    this.id = id; 
  } 
}

Isso é tudo sobre o conteúdo de comparação em Java que falamos hoje. Se você tiver outras perguntas, pode discutir na área de comentários abaixo. Obrigado pelo seu apoio.

Declaração: O conteúdo deste artigo é extraído da Internet, pertence ao autor original, foi contribuído e carregado voluntariamente pelos usuários da Internet, este site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidade legal. Se você encontrar conteúdo suspeito de infringir direitos autorais, por favor, envie e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @ para denunciar e forneça provas relevantes. Atingido, o site deletará imediatamente o conteúdo suspeito de infringir direitos autorais.)

Você também pode gostar