이 영역을 누르면 첫 페이지로 이동
Arc 블로그의 첫 페이지로 이동

Arc

페이지 맨 위로 올라가기

Arc

[Java] hashCode()

  • 2023.07.02 00:42
  • Language/Java
글 작성자: SeoArc

hashCode()

hashCode()는 객체, 즉 Object에 정의되어 있다. hashCode()는 객체의 주소 값을 변환하여 생성한 고유한 정수 값이다.

만약 같은 객체를 참조하고 있다면 hashCode 값은 동일하게 나온다.

 

예시로, 다음과 같이 작성하여 출력해보면 정수값을 확인해볼 수 있다.

class Person { }

public class Test {
    public static void main(String[] args) {
        System.out.println(new Person().hashCode()); // ex) 798154996
    }
}

Java의 모든 객체의 최상위 부모는 Object이므로 hashCode() 메서드를 Override하여 재정의할 수 있다.

 

 

equals()와 hashCode()를 같이 재정의?

아마 Java를 좀 공부했거나 Effective Java를 본 사람은 equals()와 hashCode()를 같이 재정의하라는 말을 들어봤을 것이다.

왜 같이 재정의 하라는 것일까?

 

먼저 hash와 관련된 Collection인 HashMap을 살펴보자

HashMap은 hashCode를 기준으로 key를 정한다.

그럼 다른 객체라도 hashCode만 같다면 key가 중복이 되는걸까?

 

다음 코드를 한 번 실행시켜보자.

import java.util.HashMap;
import java.util.Objects;

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

public class Test {

    public static void main(String[] args) {
        Person p1 = new Person("A");
        Person p2 = new Person("A");
        System.out.println(p1.equals(p2)); // false
        System.out.println(p1.hashCode()); // 96
        System.out.println(p2.hashCode()); // 96

        HashMap<Person, Integer> hashMap = new HashMap<>();
        hashMap.put(p1, 1);
        hashMap.put(p2, 2);

        System.out.println(hashMap.size()); // 2
        System.out.println(hashMap.get(p1)); // 1
        System.out.println(hashMap.get(p2)); // 2
    }
}

위 코드는 hashCode()를 override하여 name이 같다면 같은 hashCode가 뜨도록 했다.

하지만 hashMap에서는 각자 다른 key로 인식하는 것을 볼 수 있다.

즉 hashMap은 같은 hashCode더라도 다른 객체라면 다른 key로 처리를 해주는 것이다.

 

Java의 hashMap은 hashCode가 같은 key를 LinkedList로 묶어 처리했었다.
하지만 Java 8 이후 BinaryTree로 바뀌었다. 자세한 내용은 다음 링크를 참고하자.
https://dzone.com/articles/hashmap-performance

 

그럼 같은 객체지만 hashCode만 다른 경우는 어떻게 될까?

import java.util.HashMap;
import java.util.Objects;

class Person {
    private String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name);
    }
}

public class Test {
    public static void main(String[] args) {
        Person p1 = new Person("A");
        Person p2 = new Person("A");
        System.out.println(p1.equals(p2)); // true
        System.out.println(p1.hashCode()); // 798154996
        System.out.println(p2.hashCode()); // 681842940

        HashMap<Person, Integer> hashMap = new HashMap<>();
        hashMap.put(p1, 1);
        hashMap.put(p2, 2);

        System.out.println(hashMap.size()); // 2
        System.out.println(hashMap.get(p1)); // 1
        System.out.println(hashMap.get(p2)); // 2
    }
}

위 코드는 equals를 override하여 name이 같다면 true가 뜨도록 하였다.

역시 hashCode가 다르기 때문에 각각 따로 저장되는 것을 볼 수 있다.

 

같은 key로 처리 되려면 같은 객체(equals()가 true)이면서 hashCode가 같아야 한다는 것을 알 수 있다.

때문에 equals()를 재정의한다면 hashCode()도 같이 재정의하는 것이 좋다.

 

 

String에서의 hashCode()

우리가 많이 사용하는 String 객체의 hashCode는 어떻게 될까?

import java.util.HashMap;

public class Test {
    public static void main(String[] args) {
        String s1 = new String("ABC");
        String s2 = new String("ABC");
        System.out.println(s1.equals(s2)); // true
        System.out.println(s1.hashCode()); // 64578
        System.out.println(s2.hashCode()); // 64578

        HashMap<String, Integer> hashMap = new HashMap<>();
        hashMap.put(s1, 1);
        hashMap.put(s2, 2);

        System.out.println(hashMap.size()); // 1
        System.out.println(hashMap.get(s1)); // 2
        System.out.println(hashMap.get(s2)); // 2
    }
}

위와 같이 String은 문자열이 같다면 hashCode가 똑같이 뜨는 것을 볼 수 있다.

그럼 어떻게 정의되어 있길래 같은 값이 나오는걸까?

 

public int hashCode() {
    int h = hash;
    if (h == 0 && !hashIsZero) {
        h = isLatin1() ? StringLatin1.hashCode(value)
                       : StringUTF16.hashCode(value);
        if (h == 0) {
            hashIsZero = true;
        } else {
            hash = h;
        }
    }
    return h;
}

인코딩 타입마다 다르게 정의되어 있다. 여기선 Latin1의 hashCode()를 살펴보자

 

그럼 다음과 같이 구현되어 있는 것을 확인할 수 있다.

public static int hashCode(byte[] value) {
    int h = 0;
    for (byte v : value) {
        h = 31 * h + (v & 0xff);
    }
    return h;
}

보면 한 글자마다 31씩 곱하여 더하는 것을 볼 수 있다.

즉, 객체의 주소 값을 기준으로 hashCode를 생성하지 않기 때문에 문자열이 같으면 같은 hashCode값이 나오는 것이다.

 

* 왜 31인가?
https://github.com/dolly0920/Effective_Java_Study/issues/25
위 글을 통해 자세히 살펴볼 수 있다. 홀수이며 소수인 수를 관행적으로 사용한 것 같다.

 

 

이렇게 hashCode에 대해서 살펴보았다.

만약 equals()를 재정의 할 일이 있다면, 꼭 hashCode()도 같이 재정의 해주도록 하자

내가 추천하는 방식은 Lombok의 @EqualsAndHashCode를 사용하는 것이다.

저작자표시 (새창열림)

'Language > Java' 카테고리의 다른 글

[Java] 제네릭(Generic)  (0) 2024.03.12
[Java] Mockito  (0) 2023.07.31
[Java] Java 버전 별 특징  (2) 2023.04.14
[Java] Java 11 특징  (0) 2023.04.13
[Java] Java 9 특징  (0) 2023.04.13

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • [Java] 제네릭(Generic)

    [Java] 제네릭(Generic)

    2024.03.12
  • [Java] Mockito

    [Java] Mockito

    2023.07.31
  • [Java] Java 버전 별 특징

    [Java] Java 버전 별 특징

    2023.04.14
  • [Java] Java 11 특징

    [Java] Java 11 특징

    2023.04.13
다른 글 더 둘러보기

정보

Arc 블로그의 첫 페이지로 이동

Arc

  • Arc의 첫 페이지로 이동

검색

메뉴

  • 홈
  • 태그
  • 방명록

카테고리

  • 분류 전체보기 (106)
    • Language (28)
      • C++ (0)
      • C# (0)
      • Java (28)
    • Algorithm (47)
      • Algorithm (15)
      • Data Structure (6)
      • PS (26)
    • Computer Science (22)
      • Design Pattern (1)
      • Network (14)
      • OS (7)
    • Game (0)
      • Unity (0)
    • Backend (3)
      • Spring (1)
      • JPA (2)
    • DB (0)
      • SQL (0)
    • DevOps (2)
      • AWS (0)
      • Docker (2)
      • Jenkins (0)
      • Nginx (0)
    • Software Engineering (4)
      • OOP (4)
    • AI (0)
      • Machine Learning (0)
    • Others (0)

최근 글

인기 글

댓글

공지사항

아카이브

태그

  • graph
  • algorithm
  • network
  • 그래프
  • 알고리즘
  • 네트워크
  • 자바
  • java

나의 외부 링크

정보

SeoArc의 Arc

Arc

SeoArc

블로그 구독하기

  • 구독하기
  • RSS 피드

방문자

  • 전체 방문자
  • 오늘
  • 어제

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / Kakao. © SeoArc. Designed by Fraccino.

티스토리툴바