1. 자바에서 객체 비교하기

Java에서 객체는 참조와 값 모두로 비교할 수 있습니다.

참조 비교

두 변수가 메모리에서 동일한 개체를 가리키는 경우 이러한 변수에 저장된 참조는 동일합니다. 등호 연산자( )를 사용하여 이러한 변수를 비교하면 ==참이 되고 그 결과는 의미가 있습니다. 여기에서는 모든 것이 간단합니다.

암호 콘솔 출력
Integer a = 5;
Integer b = a;
System.out.println(a == b);


true

값으로 비교

그러나 두 개의 변수가 동일한 두 개의 개별 개체를 참조하는 상황이 자주 발생할 수 있습니다. 예를 들어 동일한 텍스트를 포함하는 두 개의 서로 다른 문자열 개체입니다.

서로 다른 객체가 동일한지 여부를 확인하려면 이 equals()방법을 사용하십시오. 예를 들어:

암호 콘솔 출력
String a = new String("Hello");
String b = new String("Hello");
System.out.println(a == b);
System.out.println(a.equals(b));


false
true

방법 은 클래스 equals에 국한되지 않습니다 String. 모든 수업이 있습니다.

직접 작성하는 수업도 여기에 그 이유가 있습니다.



2. Object클래스

Java의 모든 클래스는 Object클래스를 상속합니다. Java 제작자는 이 접근 방식을 고안했습니다.

그리고 클래스가 Object클래스를 상속하면 클래스의 모든 메서드를 얻습니다 Object. 그리고 이것은 상속의 주요 결과입니다.

즉, Object코드에서 언급하지 않더라도 모든 클래스에는 클래스의 메서드가 있습니다.

이러한 상속된 메서드에는 개체 비교와 관련된 메서드가 포함됩니다. 다음은 equals()hashCode()방법입니다.

암호 실제로는 다음과 같습니다.
class Person
{
   String name;
   int age;
}
class Person extends Object
{
   String name;
   int age;

   public boolean equals(Object obj)
   {
      return this == obj;
   }

   public int hashCode()
   {
      return address_of_object_in_memory; // This is the default implementation, but there may be a different implementation
   }
}

위의 예에서는 Person이름과 연령 매개변수가 있는 간단한 클래스를 만들었지만 단일 메서드는 만들지 않았습니다. 그러나 모든 클래스가 Object클래스를 상속하기 때문에 Person클래스에는 자동으로 두 가지 메서드가 있습니다.

방법 설명
boolean equals(Object obj)
현재 개체와 전달된 개체를 비교합니다.
int hashCode()
현재 객체의 해시코드를 반환

절대적으로 모든 개체에는 메서드가 있으며 equals다른 유형의 개체는 서로 비교할 수 있습니다. 이러한 코드는 완벽하게 컴파일되고 작동합니다.

암호 콘솔 출력
Integer a = 5;
String s = "Hello";
System.out.println(a.equals(s));
System.out.println(s.equals(a));


false
false
Object a = new Integer(5);
Object b = new Integer(5);
System.out.println(a.equals(b)) ;


true

3. equals()방법

equals()클래스에서 상속된 메서드는 현재 Object개체를 전달된 개체와 비교하기 위한 가장 간단한 알고리즘을 구현합니다. 즉, 개체에 대한 참조만 비교합니다.

Person메서드를 호출하는 대신 변수 만 비교하면 동일한 결과를 얻을 수 있습니다 equals(). 예:

암호 콘솔 출력
Person a = new Person();
a.name = "Steve";

Person b = new Person();
b.name = "Steve";

System.out.println(a == b);
System.out.println(a.equals(b));






false
false

메서드 equals가 에서 호출 되면 변수 a에 저장된 참조 a와 변수에 저장된 참조를 단순히 비교합니다 b.

그러나 비교는 String클래스에 대해 다르게 작동합니다. 왜?

클래스 를 만든 사람들이 String메서드 구현을 직접 작성했기 때문입니다 equals().

equals()방법 의 구현

이제 클래스에서 equals 메소드의 자체 구현을 작성해 보겠습니다 Person. 크게 4가지 경우를 살펴보겠습니다.

중요한:
어떤 클래스가 equals메서드를 재정의하는지에 관계없이 항상 Object개체를 인수로 사용합니다.

시나리오 1 : 메서드가 호출된 동일한 개체 equals도 메서드에 전달됩니다 equals. 현재 개체와 전달된 개체의 참조가 같으면 메서드는 를 반환해야 합니다 true. 개체는 그 자체와 같습니다.

코드에서는 다음과 같이 표시됩니다.

암호 설명
public boolean equals(Object obj)
{
   if (this == obj)
    return true;

   // The rest of the code of the equals method
}


참조 비교

시나리오 2 : 메서드 null에 전달됨 equals— 비교할 항목이 없습니다. 메서드가 호출 되는 개체는 확실히 null이 아니므로 이 경우 equals반환해야 합니다 .false

코드에서는 다음과 같이 표시됩니다.

암호 설명
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   // The rest of the code of the equals method
}


참조 비교


전달된 개체입니까 null?

시나리오 3 : a가 아닌 개체에 대한 참조가 메서드 Person에 전달됩니다 equals. 객체 가 객체 Person가 아닌 것과 같은가 Person? Person그것은 클래스의 개발자가 원하는 대로 결정할 문제입니다 .

그러나 일반적으로 개체는 동일한 것으로 간주되기 위해 동일한 클래스에 속해야 합니다. 따라서 클래스의 객체가 아닌 다른 것이 Personequals 메서드에 전달되면 항상 를 반환합니다 false. 객체의 유형을 어떻게 확인할 수 있습니까? 맞습니다 — instanceof연산자를 사용합니다.

새 코드는 다음과 같습니다.

암호 설명
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   // The rest of the code of the equals method
}


참조 비교


전달된 개체입니까 null?


전달된 객체가 아닌 경우Person

Person4. 두 개체 비교

우리는 무엇으로 끝났습니까? 메서드의 끝에 도달했다면 Person객체 참조가 null. 따라서 이를 a로 변환 Person하고 두 개체의 관련 내부 데이터를 비교합니다. 이것이 우리의 네 번째 시나리오 입니다 .

암호 설명
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   // The rest of the code of the equals method
}


참조 비교


전달된 개체입니까 null? 전달된 객체가 Typecasting


이 아닌 경우Person


두 개체를 어떻게 비교합니까 Person? name이름( )과 나이( ) 가 같으면 동등합니다 age. 최종 코드는 다음과 같습니다.

암호 설명
public boolean equals(Object obj)
{
   if (this == obj)
      return true;

   if (obj == null)
      return false;

   if (!(obj instanceof Person))
      return false;

   Person person = (Person) obj;

   return this.name == person.name && this.age == person.age;
}


참조 비교


전달된 개체입니까 null? 전달된 객체가 Typecasting


이 아닌 경우Person


하지만 그게 다가 아닙니다.

먼저 이름 필드는 a String이므로 메서드를 호출하여 이름 필드를 비교해야 합니다 equals.

this.name.equals(person.name)

둘째, name필드는 다음과 같을 수 있습니다 null. 이 경우 호출할 수 없습니다 equals. 다음에 대한 추가 확인이 필요합니다 null.

this.name != null && this.name.equals(person.name)

즉, name 필드가 null두 객체 모두에 있으면 Person이름은 여전히 ​​동일합니다.

네 번째 시나리오의 코드는 다음과 같습니다.

Person person = (Person) obj;

if (this.age != person.age)
   return false;

if (this.name == null)
   return person.name == null;

return this.name.equals(person.name);


연령이 같지 않으면
즉시 와 같으면 return false

방법 을 사용하여 비교할 의미가 없습니다 . 여기서 두 번째 필드는 와 같 거나 그렇지 않습니다. 메서드 를 사용하여 두 이름 필드를 비교합니다 . this.namenullequalsnamenull

equals


5. hashCode()방법

두 개체의 모든 필드에 대한 자세한 비교를 수행하기 위한 메서드 외에도 equals부정확하지만 매우 빠른 비교에 사용할 수 있는 또 다른 메서드가 있습니다 hashCode().

수천 개의 단어 목록을 알파벳순으로 정렬하고 단어 쌍을 반복적으로 비교해야 한다고 상상해 보십시오. 그리고 단어는 길고 많은 글자로 구성되어 있습니다. 일반적으로 말하자면, 그러한 비교는 매우 오랜 시간이 걸립니다.

그러나 가속화될 수 있습니다. 서로 다른 문자로 시작하는 단어가 있다고 가정해 보겠습니다. 서로 다르다는 것이 즉시 분명해집니다. 그러나 같은 글자로 시작한다면 결과가 어떻게 될지 아직 말할 수 없습니다. 단어가 같거나 다를 수 있습니다.

hashCode()방법은 유사한 원리를 사용하여 작동합니다. 개체에서 호출하면 단어의 첫 글자와 유사한 숫자를 반환 합니다. 이 숫자에는 다음과 같은 속성이 있습니다.

  • 동일한 개체는 항상 동일한 해시 코드를 가집니다.
  • 다른 개체는 동일한 해시 코드를 가질 수 있거나 해당 해시 코드가 다를 수 있습니다.
  • 개체에 다른 해시 코드가 있는 경우 개체는 확실히 다릅니다.

이를 더욱 명확하게 하기 위해 이러한 속성을 단어로 재구성해 보겠습니다.

  • 동일한 단어는 항상 첫 글자가 동일합니다.
  • 단어마다 첫 글자가 같거나 첫 글자가 다를 수 있습니다.
  • 단어의 첫 글자가 다르면 단어는 확실히 다릅니다

마지막 속성은 개체 비교를 가속화하는 데 사용됩니다.

먼저 두 개체의 해시 코드가 계산됩니다. 이러한 해시 코드가 다르면 개체가 확실히 다르므로 더 이상 비교할 필요가 없습니다.

그러나 해시코드가 같으면 여전히 equals 메서드를 사용하여 개체를 비교해야 합니다.



6. 코드 계약

위에서 설명한 동작은 Java의 모든 클래스에서 구현해야 합니다. 컴파일 중에는 개체가 올바르게 비교되었는지 여부를 확인할 방법이 없습니다.

Java 프로그래머는 equals() 메서드의 자체 구현을 작성하여 표준 구현(클래스에서 Object)을 재정의하는 hashCode()경우 앞서 언급한 규칙이 만족하는.

이 배열을 계약 이라고 합니다 .

equals()클래스에서 해당 메서드 만 구현하는 경우 hashCode()계약을 완전히 위반하는 것입니다(계약을 위반한 것입니다). 이러지 마.

다른 프로그래머가 귀하의 코드를 사용하면 제대로 작동하지 않을 수 있습니다. 또한 위의 계약 준수에 의존하는 코드를 사용하게 됩니다.

중요한!

요소를 검색할 때 모든 Java 컬렉션은 먼저 개체의 해시 코드를 비교한 다음 메서드를 사용하여 비교를 수행합니다 equals.

즉, 자신의 클래스에 equals메서드를 제공하지만 자신의 메서드를 작성하지 않거나 hashCode()잘못 구현하면 컬렉션이 개체에서 제대로 작동하지 않을 수 있습니다.

예를 들어 목록에 개체를 추가한 다음 메서드를 사용하여 개체를 검색할 수 contains()있지만 컬렉션에서 개체를 찾지 못할 수 있습니다.