본문 바로가기

공부방/JAVA

equals, hashcode

배열에 대한 공부를 하는데 문자열 비교와 배열 복사에 대한 내용을 만났다.

관련하여 이전에 공부했던 개념을 리마인드도 하고 새롭게 문서를 찾아 보며 공부를 또 하면 좋을 것 같아 해당 주제에 대해 조사해보았다.

equals()

우선 정말 좋은 원본 글을 또 망개님이 다시 정리한 글이 있어 해당 글을 참고했다는 것을 미리 말한다.

equals와 hashCode는 Object 클래스에 정의되어 있다.

따라서 Java의 모든 객체는 이 두 함수를 상속받고 있게 된다.

그래서 equals() 란?

  • 2개의 객체가 동일한지 검사하기 위해 사용
  • 2개의 객체가 가리키는 곳이 동일한 메모리 주소일 경우에만 동일한 객체this == obj 가 주소를 비교하는 것이다.

하지만 개발을 하다 보면 equals()는 내 의도와는 다른 결과를 내뱉는 것을 볼 수 있다.

String s1 = new String("Test");
String s2 = new String("Test");

System.out.println(s1 == s2);			// false
System.out.println(s1.equals(s2));		// true;

개발자가 보기에는 s1과 s2는 모두 똑같이 “Test” 문자열이니 == 비교를 하면 true가 나오는 걸 원할 것이다.

하지만 결과를 보면 fasle가 출력된다.

  • 그래서 우린 eqauls() 를 오버라이딩을 통해 수정을 할 필요가 있다.
  • // equals, overridden in String Class public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }

그러면 결국 우리가 원하는 true 값을 볼 수 있게 된다.

그럼 hashCode() 는?

아래 사진을 보면 native int 가 앞에 붙은 것을 볼 수 있다. 우선 정의를 말해보면,

  • 실행 중에(runtime) 객체의 유일한 integer 값을 반환하며,
  • Object 클래스에서는 heap에 저장된 객체의 메모리 주소를 반환한다. (항상은 아님)
  • hashTable 같은 자료구를 사용시 데이터가 저장되는 위치를 결정하기 위해 사용된다.

native 키워드는

  • 메서드가 JNI(Java Native Interface)라는 native code를 이용해 구현되었음을 의미한다.
  • 메서드에만 적용가능한 제어자로,
  • C/C++ 같이 Java가 아닌 언어로 구현된 부분을 JNI를 통해 java에서 이용하고자 할 때 사용된다.
    • 우리 같은 개발자는 어디에서도 사용할 수 없다..ㅎㅎ

하지만 이것 역시 개발자의 의도와는 다르게 hashSet, hashTable에 개체가 저장되는 것을 볼 수 있다. 차근차근 예를 살펴보자.

Employee라는 클래스가 있다고 하자. id는 고유값을 갖게 된다.

public class Employee{
    private Integer id;
    private String firstname;
    private String lastName;
    private String department;
 
    //Setters and Getters
}

아래 출력을 true로 하고 싶다면?

Employee e1 = new Employee();
Employee e2 = new Employee();

e1.setId(100);
e2.setId(100);

System.out.println(e1.equals(e2));  //false
  • equals() 를 오버라이딩 하면 된다.
  • public boolean equals(Object o) { if(o == null) { return false; } if (o == this) { return true; } if (getClass() != o.getClass()) { return false; } Employee e = (Employee) o; return (this.getId() == e.getId()); }

이제 HashSet에 해당 개체를 저장해보자.

Set<Employee> employees = new HashSet<Employee>();
employees.add(e1);
employees.add(e2);
 
System.out.println(employees);  //Prints two objects

서로 같은 2개의 객체를 Set 자료 구조에 넣고 출력을 했는데, 2개의 객체를 출력한다.

이것은 원치 않은 결과이다. 같은 객체라고 생각했던 것을 Set에 넣는다면 1개의 객채만 저장되어야 한다.

Object 클래스의 hashCode() 메서드는 해당 메모리 주소값을 반환한다고 설명했다.

e1, e2는 다른 해시값을 반환하게 되고 HashSet에 결국 서로 다른 위치에 저장되는 것이다.

  • 따라서 hashCode()를 오버라이딩 해야한다.
  • @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + getId(); return result; }

equals(), hashCode() 오버라이딩시 가이드 라인

  • 둘을 오버라이딩 할 때 같은 attribute를 이용해라 (Employee 클래스의 id 처럼)
  • equals는 일관된 값을 제공해야 함. 개체가 수정되지 않는 한
  • a.equals(b) == true 라면, a.hashCode() == b.hashCode() 역시 true여야 한다.
  • 두 메서드는 항상 함께 오버라이딩 해야 한다.

spring을 사용하고 있다면 주의점

만약 ORM을 사용하고 있다면 오버라이딩시 주의할 점이 있다. ORM은 어떤 개체의 값을 사용하기 전 까지는 proxy라는 개념에 의해 값들이 lazy loaded가 된다.

위에서 코드 수정 전과 같이 해당 필드 값을 그대로 사용하여 비교 한다면 해당 필드가 proxy 값을 바라보고 있을 수 있다. (값을 받아오지도 않은 빈 곳을 바라보는 것)

하지만 코드 수정 후 처럼 getter를 이용한다면 실제 값을 호출하여 정확한 비교를 할 수 있게 된다.

proxy의 lazy load는 해당 값을 사용하기 전 까지는 굳이 값을 미리 불러오지 않겠다는 기능이다.

관련하여 이전에 공부했던 개념을 리마인드도 하고 새롭게 문서를 찾아 보며 공부를 또 하면 좋을 것 같아 해당 주제에 대해 조사해보았다.

equals()

우선 정말 좋은 원본 글을 또 망개님이 다시 정리한 글이 있어 해당 글을 참고했다는 것을 미리 말한다.

equals와 hashCode는 Object 클래스에 정의되어 있다.

따라서 Java의 모든 객체는 이 두 함수를 상속받고 있게 된다.

그래서 equals() 란?

  • 2개의 객체가 동일한지 검사하기 위해 사용
  • 2개의 객체가 가리키는 곳이 동일한 메모리 주소일 경우에만 동일한 객체this == obj 가 주소를 비교하는 것이다.

하지만 개발을 하다 보면 equals()는 내 의도와는 다른 결과를 내뱉는 것을 볼 수 있다.

String s1 = new String("Test");
String s2 = new String("Test");

System.out.println(s1 == s2);			// false
System.out.println(s1.equals(s2));		// true;

개발자가 보기에는 s1과 s2는 모두 똑같이 “Test” 문자열이니 == 비교를 하면 true가 나오는 걸 원할 것이다.

하지만 결과를 보면 fasle가 출력된다.

  • 그래서 우린 eqauls() 를 오버라이딩을 통해 수정을 할 필요가 있다.
  • // equals, overridden in String Class public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }

그러면 결국 우리가 원하는 true 값을 볼 수 있게 된다.

그럼 hashCode() 는?

아래 사진을 보면 native int 가 앞에 붙은 것을 볼 수 있다. 우선 정의를 말해보면,

  • 실행 중에(runtime) 객체의 유일한 integer 값을 반환하며,
  • Object 클래스에서는 heap에 저장된 객체의 메모리 주소를 반환한다. (항상은 아님)
  • hashTable 같은 자료구를 사용시 데이터가 저장되는 위치를 결정하기 위해 사용된다.

native 키워드는

  • 메서드가 JNI(Java Native Interface)라는 native code를 이용해 구현되었음을 의미한다.
  • 메서드에만 적용가능한 제어자로,
  • C/C++ 같이 Java가 아닌 언어로 구현된 부분을 JNI를 통해 java에서 이용하고자 할 때 사용된다.
    • 우리 같은 개발자는 어디에서도 사용할 수 없다..ㅎㅎ

하지만 이것 역시 개발자의 의도와는 다르게 hashSet, hashTable에 개체가 저장되는 것을 볼 수 있다. 차근차근 예를 살펴보자.

Employee라는 클래스가 있다고 하자. id는 고유값을 갖게 된다.

public class Employee{
    private Integer id;
    private String firstname;
    private String lastName;
    private String department;
 
    //Setters and Getters
}

아래 출력을 true로 하고 싶다면?

Employee e1 = new Employee();
Employee e2 = new Employee();

e1.setId(100);
e2.setId(100);

System.out.println(e1.equals(e2));  //false
  • equals() 를 오버라이딩 하면 된다.
  • public boolean equals(Object o) { if(o == null) { return false; } if (o == this) { return true; } if (getClass() != o.getClass()) { return false; } Employee e = (Employee) o; return (this.getId() == e.getId()); }

이제 HashSet에 해당 개체를 저장해보자.

Set<Employee> employees = new HashSet<Employee>();
employees.add(e1);
employees.add(e2);
 
System.out.println(employees);  //Prints two objects

서로 같은 2개의 객체를 Set 자료 구조에 넣고 출력을 했는데, 2개의 객체를 출력한다.

이것은 원치 않은 결과이다. 같은 객체라고 생각했던 것을 Set에 넣는다면 1개의 객채만 저장되어야 한다.

Object 클래스의 hashCode() 메서드는 해당 메모리 주소값을 반환한다고 설명했다.

e1, e2는 다른 해시값을 반환하게 되고 HashSet에 결국 서로 다른 위치에 저장되는 것이다.

  • 따라서 hashCode()를 오버라이딩 해야한다.
  • @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + getId(); return result; }

equals(), hashCode() 오버라이딩시 가이드 라인

  • 둘을 오버라이딩 할 때 같은 attribute를 이용해라 (Employee 클래스의 id 처럼)
  • equals는 일관된 값을 제공해야 함. 개체가 수정되지 않는 한
  • a.equals(b) == true 라면, a.hashCode() == b.hashCode() 역시 true여야 한다.
  • 두 메서드는 항상 함께 오버라이딩 해야 한다.

spring을 사용하고 있다면 주의점

만약 ORM을 사용하고 있다면 오버라이딩시 주의할 점이 있다. ORM은 어떤 개체의 값을 사용하기 전 까지는 proxy라는 개념에 의해 값들이 lazy loaded가 된다.

위에서 코드 수정 전과 같이 해당 필드 값을 그대로 사용하여 비교 한다면 해당 필드가 proxy 값을 바라보고 있을 수 있다. (값을 받아오지도 않은 빈 곳을 바라보는 것)

하지만 코드 수정 후 처럼 getter를 이용한다면 실제 값을 호출하여 정확한 비교를 할 수 있게 된다.

proxy의 lazy load는 해당 값을 사용하기 전 까지는 굳이 값을 미리 불러오지 않겠다는 기능이다.

관련하여 이전에 공부했던 개념을 리마인드도 하고 새롭게 문서를 찾아 보며 공부를 또 하면 좋을 것 같아 해당 주제에 대해 조사해보았다.

equals()

우선 정말 좋은 원본 글을 또 망개님이 다시 정리한 글이 있어 해당 글을 참고했다는 것을 미리 말한다.

equals와 hashCode는 Object 클래스에 정의되어 있다.

따라서 Java의 모든 객체는 이 두 함수를 상속받고 있게 된다.

그래서 equals() 란?

  • 2개의 객체가 동일한지 검사하기 위해 사용
  • 2개의 객체가 가리키는 곳이 동일한 메모리 주소일 경우에만 동일한 객체this == obj 가 주소를 비교하는 것이다.

하지만 개발을 하다 보면 equals()는 내 의도와는 다른 결과를 내뱉는 것을 볼 수 있다.

String s1 = new String("Test");
String s2 = new String("Test");

System.out.println(s1 == s2);			// false
System.out.println(s1.equals(s2));		// true;

개발자가 보기에는 s1과 s2는 모두 똑같이 “Test” 문자열이니 == 비교를 하면 true가 나오는 걸 원할 것이다.

하지만 결과를 보면 fasle가 출력된다.

  • 그래서 우린 eqauls() 를 오버라이딩을 통해 수정을 할 필요가 있다.
  • // equals, overridden in String Class public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }

그러면 결국 우리가 원하는 true 값을 볼 수 있게 된다.

그럼 hashCode() 는?

아래 사진을 보면 native int 가 앞에 붙은 것을 볼 수 있다. 우선 정의를 말해보면,

  • 실행 중에(runtime) 객체의 유일한 integer 값을 반환하며,
  • Object 클래스에서는 heap에 저장된 객체의 메모리 주소를 반환한다. (항상은 아님)
  • hashTable 같은 자료구를 사용시 데이터가 저장되는 위치를 결정하기 위해 사용된다.

native 키워드는

  • 메서드가 JNI(Java Native Interface)라는 native code를 이용해 구현되었음을 의미한다.
  • 메서드에만 적용가능한 제어자로,
  • C/C++ 같이 Java가 아닌 언어로 구현된 부분을 JNI를 통해 java에서 이용하고자 할 때 사용된다.
    • 우리 같은 개발자는 어디에서도 사용할 수 없다..ㅎㅎ

하지만 이것 역시 개발자의 의도와는 다르게 hashSet, hashTable에 개체가 저장되는 것을 볼 수 있다. 차근차근 예를 살펴보자.

Employee라는 클래스가 있다고 하자. id는 고유값을 갖게 된다.

public class Employee{
    private Integer id;
    private String firstname;
    private String lastName;
    private String department;
 
    //Setters and Getters
}

아래 출력을 true로 하고 싶다면?

Employee e1 = new Employee();
Employee e2 = new Employee();

e1.setId(100);
e2.setId(100);

System.out.println(e1.equals(e2));  //false
  • equals() 를 오버라이딩 하면 된다.
  • public boolean equals(Object o) { if(o == null) { return false; } if (o == this) { return true; } if (getClass() != o.getClass()) { return false; } Employee e = (Employee) o; return (this.getId() == e.getId()); }

이제 HashSet에 해당 개체를 저장해보자.

Set<Employee> employees = new HashSet<Employee>();
employees.add(e1);
employees.add(e2);
 
System.out.println(employees);  //Prints two objects

서로 같은 2개의 객체를 Set 자료 구조에 넣고 출력을 했는데, 2개의 객체를 출력한다.

이것은 원치 않은 결과이다. 같은 객체라고 생각했던 것을 Set에 넣는다면 1개의 객채만 저장되어야 한다.

Object 클래스의 hashCode() 메서드는 해당 메모리 주소값을 반환한다고 설명했다.

e1, e2는 다른 해시값을 반환하게 되고 HashSet에 결국 서로 다른 위치에 저장되는 것이다.

  • 따라서 hashCode()를 오버라이딩 해야한다.
  • @Override public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + getId(); return result; }

equals(), hashCode() 오버라이딩시 가이드 라인

  • 둘을 오버라이딩 할 때 같은 attribute를 이용해라 (Employee 클래스의 id 처럼)
  • equals는 일관된 값을 제공해야 함. 개체가 수정되지 않는 한
  • a.equals(b) == true 라면, a.hashCode() == b.hashCode() 역시 true여야 한다.
  • 두 메서드는 항상 함께 오버라이딩 해야 한다.

spring을 사용하고 있다면 주의점

만약 ORM을 사용하고 있다면 오버라이딩시 주의할 점이 있다. ORM은 어떤 개체의 값을 사용하기 전 까지는 proxy라는 개념에 의해 값들이 lazy loaded가 된다.

위에서 코드 수정 전과 같이 해당 필드 값을 그대로 사용하여 비교 한다면 해당 필드가 proxy 값을 바라보고 있을 수 있다. (값을 받아오지도 않은 빈 곳을 바라보는 것)

하지만 코드 수정 후 처럼 getter를 이용한다면 실제 값을 호출하여 정확한 비교를 할 수 있게 된다.

proxy의 lazy load는 해당 값을 사용하기 전 까지는 굳이 값을 미리 불러오지 않겠다는 기능이다.