-
Java - 기본 클래스(Objectd의 hashCode()메서드, clone()메서드)3카테고리 없음 2023. 11. 6. 11:45
hashCode()메서드
해시(hash)는 정보를 저장하거나 검색할 때 사용하는 자료구조 이다. 1)정보를 어디에 저장할 것인지, 2)어디서 가져올 것인지,해시 함수를 사용하여 구현한다. 해시 함수는 객체의 특정 정보(키 값)를 매개변수 값으로 넣으면 그 객체가 1)저장되어야 할 위치나 2)저장된 해시 테이블 주소(위치)를 반환한다.
따라서 객체 정보(키값)를 알면 해당 객체의 위치를 빠르게 검색할수 있다. 해시 함수[hash(key)]는 개발하는 프로그램 특성에 따라 다르게 구현된다.
hashCode = hash(key); //객체의 해시 코드 값(메모리 위치 값)이 반환된다.
자바에서는 인스턴스를 힙 메모리에 생성하여 관리할 때 해시 알고리즘을 사용한다. Object 클래스의 toString()메서드 원형을 다시 살펴보면 getClass().getName() + '@' + Integer.toHexString(hashCode())입니다. 즉 참조변수를 System.out.println()을 통해서 출력해서 본 16진수 숫자 값이 '해시 코드 값'(위치)이고, 이 값은 자바 가상 머신이 힙 메모리에 저장한 '인스턴스의 주소 값'이다. 즉 자바에서 두 인스턴스가 물리적으로 같다면 hashCode()메서드에서 반환하는 해시코드 값이 같아야 한다. 여기서 equals() 메서드를 재정의해 논리적으로 같은 값일때 ture를 반환 하도록 메서드를 재정의 했다면 hashCode()메서드 역시 논리적으로만 값은 값을 가질때도 같은 해시 코드 값을 반환하도록 hashCode()메서드도 재정의 해야한다.
String과 Integer 클래스의 hashCode()메서드
String 클래스의 Integer 클래스의 equal()메서드는 재정의 되었있다. 따라서 hashCode()메서드 역시 재정의 되어 있다.
package object; public class HashCodeTest { public static void main(String[] args) { String str1 = new String("abc"); String str2 = new String("abc"); //"abc"문자열의 해시 코드 값 출력 System.out.println(str1.hashCode()); //96354 System.out.println(str2.hashCode()); //96354 Integer i1 = new Integer(100); Integer i2 = new Integer(100); System.out.println(i1.hashCode()); //100 System.out.println(i2.hashCode()); //100 } }
Student 클래스에서 hashCode()메서드 재정의하기
class Student{ int studentId; String studentName; public Student(int studentId, String studentName){ this.studentId = studentId; this.studentName = studentName; } ... //해시 코드 값으로 학번을 반환 //하도록 메서드 재정의 @Override public int hashCode(){ return studentId; } } public class EqualsTest { public static void main(String[] args) { Student studentLee = new Student(100,"이진희"); Student studentLee2 = studentLee; // 주소복사 Student studentJin = new Student(100,"이진희"); //hashCode값 //두 학생은 논리적으로 같기 때문에 //같은 해시 코드 값을 반환 System.out.println(studentLee.hashCode()); //100 System.out.println(studentJin.hashCode()); //100 //실제 주소값 System.out.println(System.identityHashCode(studentLee)); //1784662007 System.out.println(System.identityHashCode(studentJin)); //997110508 } }
Student 클래스에서 hashCode()를 재정의할 때 어떤 값을 반환하도록 만드는 것이 가장 합리적일까? 논리적으로 같은 학생인지 비교하는 equals()를 재정의할 때는 학번이 같으면 true를 반환하였다. 일반적으로 hashCode()메서드를 재정의 할 때는 equals()메서드에서 논리적으로 같다는 것을 구현할 때 사용한 멤버 변수를 활용하는 것이 가장 좋다. 따라서 Student 클래스에서는 hashCode()메서드가 학번을 반환하는 것이 가장 합리적이다.
hashCoe()메서드를 재정의했을 때 실제 인스턴스의 주소 값은 System.identityHashCode()메서드를 사용하여 알수 있다.
equals()와 hashCode()를 재정의해서 studentLee와 studentJin이 반환하는 각각 메서드의 값이 같아 졌지만. 여전히 studentLee와 studentJin은 물리적으로는 다르고 논리적으로는 같은 인스턴스이다.
clone()메서드
protected Object clone();
객체 원본을 유지해 놓고 복사본을 사용한다거나, 기본 틀(prototype)의 복사본을 사용해 동일한 인스턴스를 만들어 복잡한 생성 과정을 간단히 하려는 경우에 clone()메서드을 사용할 수 있다. colne()메서드는 Object에 위 코드와 같이 선언 되어 있으며, 객체를 복제해 또 다른 객체를 반환해주는 메서드이다.
clone()메서드로 인스턴스 복제하기
package object; class Point{ int x; int y; public Point(int x, int y) { this.x = x; this.y = y; } public String toString(){ return "x =" + x +","+"y =" + y; } } //객체를 복제해도 된다는 의미로 Cloneable //인터페이스를 함께 선언 class Circle implements Cloneable{ Point point; int radius; Circle(int x, int y, int radius){ this.radius = radius; point = new Point(x,y); } public String toString(){ return "원점은 " + point + "이고, " + "반지름은" + radius+ "입니다."; } //clone()메서드를 사용할 때 발생할 수 있는 //오류를 예외 처리한다. @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } public class ObjectCloneTest { public static void main(String[] args)throws CloneNotSupportedException { Circle circle = new Circle(10,20,30); Circle copyCircle = (Circle)circle.clone(); System.out.println(circle); //원점은 x =10,y =20이고, 반지름은30입니다. System.out.println(copyCircle); //원점은 x =10,y =20이고, 반지름은30입니다. System.out.println(System.identityHashCode(circle)); //895947612 System.out.println(System.identityHashCode(copyCircle)); //846492085 } }
clone() 메서드를 사용하려면 객체를 복제해도 된다는 의미로 클래스에 Cloneable 인터페이스를 구현해야 한다. 만약 clone()메서드만 재정의하고 Cloneable 인터페이스를 명시하지 않으면 clone()메서드를 호출할 때 CloneNotSupportedException이 발생한다.
또한 위 예제의 clone() 메서드는 Object clone()메서드를 그대로 사용하고 있다. Object clone()메서드는 클래스의 인스턴스를 새로 복제하여 생성해준다. 즉 멤버 변수가 동일한 인스턴스가 다른 메모리에 새로 생성되는 것이다. 출력 결과를 보면 인스턴스의 멤버 변수 값은 같고 주소 값은 다른 copyCircle이 생성되었음을 알수 있다.
💡 tip) 마커 인터 페이스(marker interface)
Cloneable 인터페이스를 선언해도 별도로 구현해야하는 메서드가 없다. 이렇게 구현할 메서드가 없는 인터페이스를 마커 인터페이스라고 한다.[출저 - Do it! 자바 프로그래밍 입문 , 박은종]
http://www.easyspub.co.kr/20_Menu/BookView/A001/267/PUB