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

Java 비교 문제에 대한 자세한 분석

Java의 비교 문제는 매우 기본적이면서도 혼란스러운 문제 중 하나이다. 오늘은 몇 가지 잘못된 점을 상세히 정리하여, 모두의 학습과 면접에 도움이 되기를 바란다.

1. ==와 equals()의 차이

먼저, ==와 equals()의 차이를 알아야 한다. ==는 항상 주소 값을 비교하는 것이며, 기본 데이터 타입에 대해서는 == 비교는 실제로는 변수 값이 같은지 확인하는 것이며, 참조 데이터 타입에 대해서는 주소 값을 비교한다. 특히 String 타입은 ==를 자연스럽게 사용할 수 있지만, 실수하기 쉬운 부분이다. equals() 메서드는 Object 클래스의 메서드로, 모든 클래스는 Java에서 기본적으로 Object 클래스를 상속받기 때문에 모든 클래스 객체는 equals() 메서드를 가지고 있다. Object 클래스의 equals() 메서드는 다음과 같다:

소스 코드를 보면 Object 클래스의 equals() 메서드가 ==를 사용하여 루프를 사용하는 것을 알 수 있으며, 따라서 그것이 비교하는 것은 실제로는 주소 값이다. 따라서 equals() 메서드를 다른 비교를 위해 사용하고자 한다면 equals() 메서드를 재정의해야 한다.

2. 기본 데이터 타입 및 패키지 클래스

모두가 알고 있듯이, byte, short, int, long, boolean, char, double, float 이 tám개는 기본 데이터 타입이며, 이들은 스택 메모리에 저장된다. 그리고 이들을 대표하는 패키지 타입(Byte, Short, Integer, Long, Boolean, Character, Double)가 정의된 변수는 힙 메모리에 존재한다. 기본 데이터 타입에 대해서는, 비교는 상대적으로 간단하며, 같은지 확인하는 것은 ==를 사용하고, 크기를 비교하는 것은 <, >, <=, >=를 사용하면 된다. 하지만 패키지 타입에 대해서는 다르다.

먼저, 등가 여부를�断정하는 경우, 다음 코드의 실행 결과를 확인해야 합니다:

package dailytest;
import org.junit.Test;
/**
 * Java에서의 비교 요약
 * @author yrr
 */
public class JavaCompareTest {
  /**
   * Integer 타입의 등가 여부判断
   */
  @Test
  public void test01() {
    int n3 = 48;
    System.out.println("--------new 객체를 사용할 때는 값이 [-127,128] 사이에 있을 때---------");
    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("--------직접 할당 방식에서는 값이 [-128,127] 사이에 있을 때---------");
    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("--------직접 할당 방식에서는 값이 [-127,128] 사이에 있을 때---------");
    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
    //Integer.intValue() 메서드를 사용할 때는 null 여부를 확인하여 NullPointException이 발생하지 않도록 주의해야 합니다
  }
  /**
   * Long 타입의 등가 여부�断정
   */
  @Test
  public void test02() {
    //이곳에서는 long으로 정의할 때 L이나 l을 추가하지 않아도 됩니다. Long으로 정의할 때는 반드시 추가해야 하지 않으면 오류가 발생합니다
    //구축할 때 구분을 위해 추가해야 합니다
    long n3 = 48L;
    System.out.println("--------new 객체를 사용할 때는 값이 [-127,128] 사이에 있을 때---------");
    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("--------직접 할당 방식에서는 값이 [-127,128] 사이에 있을 때---------");
    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("--------직접 할당 방식에서는 값이 [-127,128] 사이에 있을 때---------");
    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
    //Long.intValue() 메서드를 사용할 때는 null 여부를 확인하여 NullPointException이 발생하지 않도록 주의해야 합니다  
  }
}

위의 실행 결과에 대해 다음과 같이 설명합니다:

먼저, new 메서드를 사용하여 Integer 또는 Long 객체를 선언하는 경우, new 객체는 모두 스택에서 공간을 할당하기 때문에, 두 값을 같게 할당해도 ==로 비교할 때 주소 값을 비교하기 때문에 false를 반환합니다. 기본 데이터类型的 包装类은 모두 equals() 메서드를 재정의하여 값의 크기를 비교합니다. 따라서 equals() 메서드를 사용하면 값을 기준으로 비교할 수 있습니다. Integer 변수와 int 변수를 비교할 때, 비교 값은 값의 크기에 기반하여 나오는 것을 발견할 수 있습니다. 이는 Integer 타입이 비교할 때 자동으로 박싱되어 int 타입으로 변환되기 때문입니다. 이전의 설명은 모든 包装类型에 적용됩니다 직접 할당 방식에서는 값이48의 두 Integer 변수에 대해 ==로 비교할 때 true로 나타나지만, 값이128이후에도 false로 나타납니다. 이는 Integer n1 = 48이렇게 직접 할당하는 방식은 Integer.value() 메서드를 호출하는 것입니다. Integer.value() 메서드의 소스 코드를 간단히 보면 다음과 같습니다:

여기서 볼 수 있듯이, 이곳에 if 조건문이 있으며, 입력된 i가 [에 포함되어 있을 때-128,127의 범위 내에서는 IntegerCache 배열에서 직접 반환됩니다. 따라서 이 범위 내의数值는 해당 배열의 주소 값을 반환합니다. 따라서 ==로 판단하면 true를 반환합니다. 이 범위 밖의 것은 new로 생성된 객체이므로 false를 반환합니다. 이 결과는 Byte, Short, Integer, Long 타입 모두에 대해 적용됩니다. (관심이 있는 경우 해당 value() 메서드의 소스 코드를 확인할 수 있습니다). Byte 타입의 범위는 [}}-128,127이렇게 되어 Byte 타입에 대해 ==와 equals()는 다를 바 없습니다. 

대소 비교를 위해 >, <, <=, >=를 사용하는 것은 문제가 없습니다. 자동 해박이 수행됩니다. 그러나 우리는 일반적으로 다음 두 가지 방식을 추천합니다:

xxxValue() 메서드를 호출하여 기본 데이터 타입으로 변환하여 비교합니다. compareTo() 메서드를 사용하여 비교합니다. 패키지 클래스에서compareTo() 메서드를 재정의했습니다. compareTo() 소스 코드를 확인하면, 실제로는 자동 해박으로 변환하여 기본 데이터 타입으로 비교하는 것을 볼 수 있습니다.

2. Java 객체 비교

위의 설명을 통해 객체 비교는 쉬워집니다. 원리는 모두 동일합니다.

1. String 타입의 비교

String 타입은 >, <=, >=, <를 직접 사용할 수 없습니다. 컴파일 예외가 발생합니다.

package dailytest;
import org.junit.Test;
/**
 * 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); //The operator < is undefined for the argument type(s) java.lang.String, java.lang.String
    System.out.println(s1.compareTo(s3) < 0);  //true
  }
}

 2. 객체 비교

클래스 객체 비교 결과도 동일하지만, 기본 데이터 타입과 String 타입에 비해 약간 복잡합니다.

某一規則에 따라 두 개의 객체가 같은지 여부를 판단하려면, 판단되는 클래스에서 equals() 메서드를 재정의해야 합니다. 예제 코드는 다음과 같습니다:

package dailytest;
import org.junit.Test;
/**
 * 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());
  }
}

또한 두 개의 객체를 비교할 때(이것은 자주 물어보는 면접 질문 중 하나입니다), 두 가지 방법이 있습니다:

비교 대상 클래스가 Comparable 인터페이스를 구현하고 compareTo() 메서드를 재정의하면, 또는 Comparator 인터페이스를 구현한 클래스를 정의하거나 내부 클래스를 사용하여 compare() 메서드를 재정의하면 됩니다. 두 방법의 차이는 다음과 같습니다: 전자는 비교 대상 클래스 내에 정의되며, 후자는 비교 대상 클래스 외에 정의됩니다. 이러한 차이로 두 방법의 장단점도 명확하게 나타납니다. 전자는 간단하지만 비교 대상 클래스를 수정해야 하며, 후자는 원래 코드를 수정하지 않아도 더 유연합니다.

첫 번째 방법, 예제 코드는 다음과 같습니다:

package dailytest;
import org.junit.Test;
/**
 * 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();
  }  
}

두 번째 방법, 예제 코드는 다음과 같습니다:

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; 
  } 
}

이제 여러분께 말씀드린 Java에서 비교 문제에 대한 내용이 모두입니다. 다른 질문이 있으면 아래 댓글 영역에서 논의해 주세요. 감사합니다.

명시: 이 글의 내용은 인터넷에서 가져왔으며, 저작권자는 누구인지 모릅니다. 내용은 인터넷 사용자가 자발적으로 기여하고 업로드한 것이며, 이 사이트는 소유권을 가지지 않으며, 인공적인 편집 처리를 하지 않았으며, 관련 법적 책임도 부담하지 않습니다. 저작권 침해가 의심되는 내용을 발견하면, 메일을 보내 주시기 바랍니다: notice#oldtoolbag.com(메일을 보내면, #을 @으로 바꿔주세요. 신고하고 관련 증거를 제공해 주시면, 사이트는 즉시 저작권 침해 내용을 삭제할 것입니다。)

추천 합니다