JavaRush /Java Blog /Random-KO /커피 브레이크 #168. Java에서 같음 및 해시코드 메서드를 재정의하는 이유는 무엇입니까?

커피 브레이크 #168. Java에서 같음 및 해시코드 메서드를 재정의하는 이유는 무엇입니까?

Random-KO 그룹에 게시되었습니다

Java에서 같음 및 해시코드 메서드를 재정의하는 이유는 무엇입니까?

출처: 중간 이 기사에서는 밀접하게 관련된 두 가지 메서드인 equals()hashcode() 에 중점을 둡니다 . 서로 상호 작용하는 방법과 이를 올바르게 재정의하는 방법을 배우게 됩니다. 커피 브레이크 #168.  Java에서 같음 및 해시코드 메서드를 재정의하는 이유는 무엇입니까?  - 1

왜 equals() 메서드를 재정의합니까?

Java에서는 == , += , -+ 와 같은 연산자의 동작을 오버로드할 수 없습니다 . 그들은 주어진 프로세스에 따라 작동합니다. 예를 들어 == 연산자 의 연산을 생각해 보세요 .

== 연산자는 어떻게 작동하나요?

비교되는 두 참조가 메모리의 동일한 인스턴스를 가리키는지 확인합니다. == 연산자는 두 참조가 메모리에서 동일한 인스턴스를 나타내는 경우에만 true로 평가됩니다. 샘플 코드를 살펴보겠습니다.
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
      }
프로그램에서 서로 다른 위치에 두 개의 Person 객체를 생성 하고 이를 비교하고 싶다고 가정해 보겠습니다.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println( person1 == person2 ); --> will print false!
비즈니스 관점에서 보면 둘은 똑같아 보이죠? 그러나 JVM의 경우에는 동일하지 않습니다. 둘 다 new 키워드를 사용하여 생성되므로 이러한 인스턴스는 서로 다른 메모리 세그먼트에 위치합니다. 따라서 == 연산자는 false 를 반환합니다 . 하지만 == 연산자를 재정의할 수 없다면 JVM에 이 두 객체를 동일하게 취급하도록 어떻게 알릴 수 있을까요? 이것이 .equals() 메소드가 작동하는 곳입니다 . 동일하다고 간주하기 위해 일부 객체가 특정 필드에 대해 동일한 값을 갖는지 확인하기 위해 equals()를 재정의할 수 있습니다 . 비교할 필드를 선택할 수 있습니다. 두 개의 Person 객체가 동일한 나이와 동일한 이름을 갖는 경우에만 동일하다고 말하면 IDE는 다음 과 같은 항목을 생성하여 자동으로 equals()를 생성합니다.
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
이전 예로 돌아가 보겠습니다.
Person person1 = new Person("Mike", 34);
Person person2 = new Person("Mike", 34);
System.out.println ( person1 == person2 ); --> will print false!
System.out.println ( person1.equals(person2) ); --> will print true!
예, 우리가 원하는 방식으로 객체를 비교하기 위해 == 연산자를 오버로드할 수는 없습니다. 하지만 Java는 우리에게 원하는 대로 재정의할 수 있는 다른 방법인 equals() 메서드를 제공합니다. 클래스에 .equals() (오버라이드라고도 함) 의 사용자 정의 버전을 제공하지 않으면 Object 클래스 의 사전 정의된 .equals()== 연산자가 동일하게 작동한다는 점을 명심하십시오 . Object 에서 상속된 기본 equals() 메소드는 비교되는 두 인스턴스가 메모리에서 동일한지 확인합니다!

hashCode() 메서드를 재정의하는 이유는 무엇입니까?

HashSetHashMap 과 같은 Java의 일부 데이터 구조는 해당 요소에 적용되는 해시 함수를 기반으로 해당 요소를 저장합니다. 해시 함수는 hashCode() 입니다 . .equals() 메서드를 재정의할 수 있는 선택권이 있다면 hashCode() 메서드를 재정의할 수도 있어야 합니다 . 여기에는 이유가 있습니다. 결국, Object 에서 상속된 hashCode()의 기본 구현은 메모리의 모든 객체를 고유한 것으로 간주합니다! 하지만 이러한 해시 데이터 구조로 돌아가 보겠습니다. 이러한 데이터 구조에는 규칙이 있습니다. HashSet에는 중복 값이 ​​포함될 수 없으며 HashMap에는 중복 키가 포함될 수 없습니다. HashSet은 각 HashSet 값이 HashMap 에 키로 저장되는 방식으로 HashMap을 사용하여 구현됩니다 . HashMap은 어떻게 작동하나요 ? HashMap은 여러 세그먼트가 있는 기본 배열입니다. 각 세그먼트에는 연결된 목록( linkedList )이 있습니다. 이 연결 목록은 키를 저장합니다. HashMap은 hashCode() 메서드를 사용하여 각 키에 대한 올바른 linkedList를 찾은 다음 해당 linkedList 의 모든 요소를 ​​반복 하고 해당 요소가 거기에 포함되어 있는지 확인하기 위해 해당 요소 각각에 equals() 메서드를 적용합니다 . 중복 키는 허용되지 않습니다. HashMap 안에 무언가를 넣으면 키는 이러한 연결 목록 중 하나에 저장됩니다. 이 키가 저장될 연결된 목록은 해당 키에 대한 hashCode() 메서드 의 결과로 표시됩니다 . 즉, key1.hashCode() 의 결과가 4이면 해당 key1은 거기에 존재하는 LinkedList 에 있는 배열의 4번째 세그먼트에 저장됩니다 . 기본적으로 hashCode() 메서드는 각 인스턴스에 대해 서로 다른 결과를 반환합니다. == 처럼 동작하고 메모리의 모든 인스턴스를 다른 객체로 처리하는 기본 equals()가 있으면 문제가 없습니다. 기억하실 수 있듯이, 이전 예에서 Person 인스턴스의 나이와 이름이 동일하면 동일한 것으로 간주되기를 원한다고 말했습니다 . 커피 브레이크 #168.  Java에서 같음 및 해시코드 메서드를 재정의하는 이유는 무엇입니까?  - 2
Person person1 = new Person("Mike", 34);
    Person person2 = new Person("Mike", 34);
    System.out.println ( person1.equals(person2) );  --> will print true!
이제 이러한 인스턴스를 특정 문자열을 값 쌍으로 사용하여 키로 저장하는 맵을 생성해 보겠습니다.
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
Person 클래스 에서는 hashCode 메소드를 오버라이드하지 않았지만 , equals 메소드를 오버라이드했습니다 . 기본 hashCode는 person1.hashCode()person2.hashCode() 의 다양한 Java 인스턴스에 대해 서로 다른 결과를 제공하므로 다른 결과를 얻을 가능성이 높습니다. 우리 지도는 서로 다른 연결 목록에 있는 서로 다른 사람 으로 끝날 수 있습니다 . 이는 HashMap 의커피 브레이크 #168.  Java에서 같음 및 해시코드 메서드를 재정의하는 이유는 무엇입니까?  - 삼 논리에 위배됩니다 . 결국, HashMap은 여러 개의 동일한 키를 가질 수 없습니다! 요점은 Object 클래스 에서 상속된 기본 hashCode() 로는 충분하지 않다는 것입니다. Person 클래스 의 equals() 메서드를 재정의한 후에도 마찬가지입니다 . 그렇기 때문에 equals 메서드를 재정의한 후에 hashCode() 메서드를 재정의해야 합니다 . 이제 이 문제를 해결해 보겠습니다. hashCode() 메소드를 재정의하여 equals() 와 동일한 필드 , 즉 agename을 고려하도록 해야 합니다 .
public class Person {
      private Integer age;
      private String name;

      ..getters, setters, constructors
@Override
public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                name.equals(person.name);
    }
@Override
public int hashCode() {
        int prime = 31;
        return prime*Objects.hash(name, age);
    }
hashCode() 메서드 에서는 간단한 값을 사용했습니다(다른 값을 사용할 수 있음). 그러나 문제를 덜 일으키려면 소수를 사용하는 것이 좋습니다. HashMap 에 이 키를 다시 저장해 보겠습니다 .
Map<Person, String> map = new HashMap();
map.put(person1, "1");
map.put(person2, "2");
person1.hashCode()person2.hashCode()는 동일합니다. 0이라고 가정해 보겠습니다. HashMap은 세그먼트 0으로 이동하고 그 안에 LinkedList는 값이 "1"인 키로 person1을 저장합니다 . 두 번째 경우, HashMap이 person2 키를 "2" 값으로 저장하기 위해 다시 버킷 0으로 이동하면 그와 동일한 다른 키가 이미 존재한다는 것을 알게 됩니다. 이렇게 하면 이전 키를 덮어쓰게 됩니다. 그리고 HashMap 에는 핵심 person2 만 존재하게 됩니다 . 커피 브레이크 #168.  Java에서 같음 및 해시코드 메서드를 재정의하는 이유는 무엇입니까?  - 4이것이 우리가 여러 개의 동일한 키를 사용할 수 없다는 HashMap 규칙의 작동 방식을 배운 방법입니다 ! 그러나 동일하지 않은 인스턴스는 동일한 해시코드를 가질 수 있으며 동일한 인스턴스는 동일한 해시코드를 반환해야 한다는 점을 명심하세요.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION