ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JAVA] hashCode & equals
    study/java 2020. 9. 2. 00:12

    Object 클래스에는 hashCode() 메서드와 equals() 메서드가 정의되어 있다.

    Object 클래스에 있는 hashCode() 메서드는 자바 홈페이지에 이렇게 정의되어 있다.

    Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by HashMap

    객체의 해시 코드값을 반환합니다. 이 메서드는 HashMap에서 제공하는 것과 같은 해시 테이블의 이점을 위해 지원됩니다.

     

    즉, HashTable이나 HashMap은 hashcode를 이용함으로써 객체를 저장하는 다른 컬렉션들에 비해 장점을 갖게된다는 것이다.

    장점은 어떤 장점일까?

    Key의 hashcode를 통해서 value값을 더욱 쉽게 찾아낼 수 있다는 것이다.

    public class Member {
        
        private String name;
        private int age;
    
        public Member(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    }

     

    public class Test {
        public static void main(String[] args) {
            Object object = new Object();
            System.out.println("object의 참조 변수 값 : " + object);
            System.out.println("object객체의 hashcode 값 : " + object.hashCode());
            System.out.println("------------------------------");
    
            Member member = new Member("jaeho", 26);
            System.out.println("member의 참조 변수 값 : " + member);
            System.out.println("member객체의 hashcode 값 : " + member.hashCode());
            System.out.println("------------------------------");
    
            String a = "자바";
            System.out.println("자바객체의 hashcode 값 : " + a.hashCode());
        }
    }
    
    
    //결과
    object의 참조 변수 값 : java.lang.Object@1b6d3586
    object객체의 hashcode 값 : 460141958
    ------------------------------
    member의 참조 변수 값 : Member@4554617c
    member객체의 hashcode 값 : 1163157884
    ------------------------------
    자바객체의 hashcode 값 : 1631876

     

    정리하자면 hashCode() 메서드는 각 객체에 대응되는 고유한 정수값을 리턴하게 된다.

     

    그런데 현재 hashCode() 메서드는 Object 클래스에서 정의된 그대로의 메서드이다.

    또한 Member 클래스를 정의할 때 hashCode()를 오버라이드하지 않고 그대로 사용하였다.

    하지만 예외인 경우가 있다.

     

    바로 String 클래스이다.

    String의 경우 hashCode()메서드를 오버라이드하여 새롭게 정의해놓았다.

    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
        
        private final char value[];
        private int hash; // Default to 0
        private static final long serialVersionUID = -6849794470754667710L;
        
        ...
        
        public int hashCode() {
            int h = hash;
            if (h == 0 && value.length > 0) {
                char val[] = value;
    
                for (int i = 0; i < value.length; i++) {
                    h = 31 * h + val[i];
                }
                hash = h;
            }
            return h;
        }
        
        ...
    }

     

    위의 오버라이드한 코드에 따르면 String은 문자열이 같다면 같은 hashcode를 갖게 된다.

    그 이유는 String의 경우 new String()을 통해 객체를 생성하든, 변수에 바로 문자열을 넣어주든 문자열의 내용이 같다면 같은 문자열인 것이다.

    String str = "자바";
    String str2 = new String("자바");

    같은 문자열을 갖는 두 객체의 hashcode가 다르다면 hashcode의 의미가 없어지게 된다.

    hashcode를 활용해서 Map에 저장된 key 값을 찾아야 하는데 같은 객체임에도 불구하고 hashcode 값이 다르면 제대로 찾을 수가 없게 된다.

    그러므로 인위적으로 String 클래스 안에서 hashCode() 메서드를 재정의하여 같은 String 객체에 대해서는 hashcode가 같아지도록 만들어준것이다.

     

    이제 equals()와 hashCode()의 관계에 대해서 알아보자.

    아래의 예제를 살펴보자.

    Object object1 = new Object();
    Object object2 = new Object();
    Object object3 = new Object();
    Object object4 = new Object();
    object1.equals(object2)는 false
    object1.equals(object3)는 false
    object1.equals(object4)는 false
    object2.equals(object3)는 false
    object2.equals(object4)는 false
    object3.equals(object4)는 false
    
    366712642
    1829164700
    2018699554
    1311053135

    4개의 객체는 모두 equals()메서드로 비교했을때 false를 리턴했다.

    즉, 다른 객체라는 뜻이다.

    또한 4개의 객체는 모두 서로 다른 hashcode를 리턴했다.

     

    String str1 = "안녕";
    String str2 = "안녕";
    String str3 = new String("안녕");
    String str4 = new String("안녕");
    str1.equals(str2)는 true
    str1.equals(str3)는 true
    str1.equals(str4)는 true
    str2.equals(str3)는 true
    str2.equals(str4)는 true
    str3.equals(str4)는 true
    
    1611021
    1611021
    1611021
    1611021

    위의 String 예제를 보면 모두 "안녕"이라는 같은 문자열을 만들었으므로 위 4개의 객체는 모두 같은 객체다. ( 주소값이 다를 수는 있다.)

     

    아래 예제를 보자

    public class Member {
        
        private String name;
        private int age;
    
        public Member(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    }

    아래 두 개의 객체를 생성했다.

    Memeber member1 = new Member("jaeho", 26);
    Memeber member2 = new Member("jaeho", 26);
    
    
    //결과
    member1.equals(member2)는 false

    위 결과를 보면 Member 객체의 생성자에 같은 값을 주입해줬음에도 불구하고 두 객체는 다르다는 결론이 나온다.

    하지만 위 두 객체는 특성상 같은 객체여야한다.

     

    즉, 새롭게 정의한 클래스에서는 두 객체가 같은 객체임을 의미하는 equals 메서드를 수정해야할 필요성이 생기게 된다.

    public class Member {
        
        private String name;
        private int age;
    
        public Member(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
        
        @Override
        public boolean equals(Object obj){
            if(obj instanceof Member){
                Member member = (Member) obj;
                return (age==member.age) && (name.equals(member.name));
            }else{
                return false;
            }
    }
    Memeber member1 = new Member("jaeho", 26);
    Memeber member2 = new Member("jaeho", 26);
    
    
    //결과
    member1.equals(member2)는 true

    위의 코드처럼 equals를 오버라이드하면 아까 두 객체는 같다는 결과가 나온다.

     

    하지만 두 객체의 hashcode는 다르게 나온다.

    당연한 결과다

    우리는 이전의 예제로 equals 메서드를 오버라이드하기 전에 두 객체의 hashcode가 다르다는 것을 이미 알고 있다.

    Object의 hashCode 메서드는 그대로 사용하고 equals 메서드만 재정의해주었기 때문이다.

    이는 hashcode의 의미를 해치는 것이다.

    Member 클래스에서 위 두 객체는 같은 객체이므로 hashcode도 같아야만 hashcode로써의 의미를 갖는 것이다.

    따라서 hashCode 메서드도 오버라이드해주어야 한다.

     

    equals(Object)메소드가 true이면 두 객체의 hashCode 값은 같아야 한다.

    equals(Object)메소드가 false이면 두 객체의 hashCode가 꼭 다를 필요는 없다.

    하지만 서로 다른 hashCode 값이 나오면 해시 테이블(hash table)의 성능이 향상될 수 있다는 점은 이해하고 있어야 한다.

     

    참고

    http://blog.naver.com/PostView.nhn?blogId=travelmaps&logNo=220930144030&parentCategoryNo=&categoryNo=9&viewDate=&isShowPopularPosts=false&from=postView

    'study > java' 카테고리의 다른 글

    [JAVA] Exception  (0) 2020.08.21
    [JAVA] 제네릭  (0) 2020.08.08
    [JAVA] GC  (0) 2020.08.06
    [JAVA] Jar,War  (0) 2020.08.04
    [JAVA] static 키워드  (0) 2020.07.30
Designed by Tistory.