Dipa's document :: Dipa's document


이 메소드는 해싱(hashing)기법에 사용되는 '해쉬함수(hash function)'를 구현 한 것이다. 해싱은 데이터 관리기법 중의 하나인데 다량의 데이터를 저장하고 검색하는데 유용하다. (자료구조 - 해쉬참고바람)


일반적으로 해시코드가 같은 두 객체가 존재하는 것이 가능하지만, Object클레스의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 만들어 리턴하기 떄문에 객체마다 다른 값을 가지고 있다.


컬렉션부분에서의 HashSet, HashMap, Hashtable과 같은경우에서 논리적 동등 비교(데이터 비교)시 hashCode()를 overriding을 할 필요성이 있다. 

Key 객체의 주소가 달라도 같은 내용의 객체이면, hashCode()는 같은 hashCode를 리턴 해야 할 것이다. 

이유는 Key객체의 내용이 같을 떄, 같은 값의 hashCode를 리턴해야 HashTable, HashMap과 같은 것을 사용할 떄, 제대로 된 key로써의 역할을 하기 떄문이다.


먼저 equals()를 호출하기전에 hashCode()를 통한 해쉬코드의 값을 호출을 하고 비교를 하고 같으면, 다시 한번 equals()메소드를 통해 두 객체의 내부 데이터가 동등한지 확인 작업을 하고 동등객체 인지의 유무를 결정을 한다.

따라서, 사용자가 직접 만든 클래스를 Key로 사용하기 싶으면, 반드시 hashCode()를 overridung를 해야한다.


아래의 소스 코드를 보면

public class ObjectEqualsMethod {

public class Key {
	private int number;
	
	public Key(int number) {
		this.number = number;
	}

	public int getNumber() {
		return number;
	}

	public void setNumber(int number) {
		this.number = number;
	}
	
	@Override
	public boolean equals(Object obj) {
		if(obj instanceof Key) {
			Key compareKey = (Key)obj;
			if(this.number == compareKey.number) {
				return true;
			}
		}
		return false;
	}
	
    }
}
import java.util.HashMap;

public class KeyExample {
	public static void main(String[] args) {
		
		//Key객체를 식별키로 사용해서 String 값을 저장하는 HashMap 객체를 생성 
		HashMap<Key,String> hashMap = new HashMap<Key,String>();
		
		hashMap.put(new Key(1), "홍길동");

		System.out.println((hashMap.get(new Key(1))));
	}
}

Key값으로 Key객체를 사용하였는데도, 결과는 null이라는 충격적인 결말이 나온다. 이는 hashCode()를 재정의를 하지를 않아, Object클래스의 

hashCode()를 사용을 하기 떄문에 저장하고, 불러오는 과정의 Key객체는 서로 다른 객체로 판별이 되어 null값으로 처리가 된다. 


결국 Object클레스의 hashCode()를 재정의를 하여여 한다는 결론이 나온다.


HashSet, HashMap, Hashtable은 해시코드 값이 다르면 다른 객체로 판단하고, 해시코드 값이 같으면 equals()메소드를 다시 비교한다. 그렇기 떄문에 hashCode() 메소드가 true가 나와도 equals()의 리턴값이 다르면 다른 객체가 된다. 

Posted by SH후니
,

블로그에 빨리올려야지 했는데..벌써 한달이 가까운시간을 안하고 이제서야 올린다,,,ㅡㅡ 반성중(_ _)

이번에는 Object클레스에 대해서 공부해보고자 한다.

Object클래스는 Java.lang패키지에 있는 클래스로 Java.lang패키지는 자바 프로그램의 기본적인 클래스를 담고있는 패키지 이다. 그중에서도 Object클래스 자바 클래스의 최상이 클래스 이기떄문에 중요한 클래스라고 생각을 한다.

따라서, 클래스를 선언을 하고 extends를 통해서 다른 클래스를 상속을 받지 않으면, 암시적으로 java.lang패키지의 Object클래스를 상속을 받게 된다. 

자바의 모든 클래스는 Object클래스의 자식클래스이자 자손 클래스이다. Object클래스는 메소드들로만 구성이 되어 있는데 이 메소드들은 모든 클래스가 Object클래스를 상속을 받고 있으므로 모든 클래스에 사용이 가능하다.

Object 클래스의 메소드에는 equals(), hashCode(), toString() 등등이 있다.


* equals()

 결론부터 예기를 하면 Object 클래스의 equals()는 '==' 연산자와 동일한 기능을 한다. 같은 객체일 경우에는 true를 리턴을 하고, 객체가 다를경우에는 false를 리턴을 한다. 그렇다면 여기서 의문을 가지는게,,, 왜 String의 문자 비교를 할떄는 equals()를 쓰는지가 의문일 것이다.

그것은 String클래스가 Object클래스의 equals()메소드를 Override(재정의)를 하였기 떄문이다. String클래스의 equals()는 Override를 통해서 논리적 동등 비교를 하는 것이다.(논리적 동등 비교란? 객체가 같은지 다른지를 비교를 하는 것이 아닌 객체가 저장하고 있는 데이터를 비교를 하는 것)

public class ObjectEqualsMethod {

	public static void main(String[] args) {
		Object object01 = new Object();
		Object object02 = new Object();

		System.out.println(object01 == object02);
		
		System.out.println(object01.equals(object02));
	}

}

위에 코드를 보면 Object의 equals()메소드는 '=='연산자와 같다는 것을 알수가 있다. 

그렇다면 String클래스를 한번 살펴 보자.

   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;
    }

위의 코드는 Object클래스의 equals()메소드를 Override를 한 것이다. 처음 조건문에서는 객체를 비교를 하는 조건문이고 두번째 조건문에서는 String클래스가 맞으면 객체 안의 데이터. 즉, 문자를 비교를 하는 소스 코드이다.  첫번쨰 조건문이 왜 있는것일까? 단순희 문자만 비교를 하면 두번째 조건문마 있므면 되지 않나? 라는 의구심을 품는 개발자도 있을것이다. 

이 부분에 대해서는 String클래스 부분에서 다시한번 포스팅을 할 것이다. 결론부터 얘기를 하면 이 두소스의 차이이다. 

public class internMethod {

	public static void main(String[] args) {
		String input01 = "홍길동";
		String input02 = new String("홍길동");
		
		System.out.println(input01.equals(input02));

	}

}


Posted by SH후니
,

자바 프로그램을 개발하고(*.java) 개발한 자바소스 파일을 컴파일(javac.exe)을 하여 컴파일이 성공을 하면 바이트 코드(*.class)파일이 생성이 된다. 

응용 프로그램을 실행을 시키기 위해서, JVM 구동 명령어(java.exe)를 통해서 JVM을 구동 시키면 JVM은 운영체제로 부터 필요한 메모리를 할당을 받는데 이를 메모리 영역(Runtime Data Area)이라고 한다.

메모리 영역에는 3가지의 주요영역 (메소드 영역 - method area 또는 Class area 또는 Static area, 힙 영역 - heap area. 스택영역 - call stack 또는 execution stack)이 있다.

* 메소드 영역 : 클래스파일의 바이트 코드가 로드 되는 곳이다.

 프로그램 실행 중 어떤 클래스가 사용이 되면, JVM은 해당 클래스들을 클래스 로더로 읽어서 클래스별로 런타일 상수풀(runtime constant pool), 필드(field), 데이터, 메소드(method) 데이터, 메소드 코드 생성자 코드 등을 분류해서 저장하고,(왜냐? 우리가 집을 정리할떄도 어떤건 어디에두고 그러지 않나? 그거랑 똑같다고 생각하면 된다) 

이 영역은 JVM이 사작할 때 생성되고 모든 스레드가 공유 하는 영역이다.

*cf : 열거상수(enum 클래스의 상수)도 메소드 영역에 저장이 되고, 메소드 영역에 생성된 열거 상수가 해당 enum의 객체를 참조하게 된다.

* 힙 영역 : 인스턴스(객체) 가 생성되는 공간으로, 프로그램 실행중 생성되는 인스턴스는 모두 이 곳에 생성이 된다. 스택 영역의 변수나 다른 객체의 필드에서 참조를하며, 참조하는 변수나 필드가 없는 객체는 의미없는 객체가 되기 떄문에 JVM은 Garbage Collector에 의해서 자동으로 자동으로 힙 영역의 객체를 제거 한다.

* 스택 영역 : 한마디로 말하면 지역변수와 매개변수가 저장이 된다고 생각을 하면된다.

각 스레드 마다 하나씩 존재하며 스레드가 시작될떄 할당이 되고, java에서 추가적으로 스레드를 생성하지 않았다면 main 스래드만 존재하므로 스택도 하나이다. 메소드의 작업에 필요한 메모리 공간(Frame)을 제공을 하고, 메소드가 호출이 되면, 호출스택에 호출된 메소드를 위한 메모리가 할당되며, 이 메모리는 메소드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용이 되고. 메소드의 작업을 마치면 할당되었던 메모리 공간은 반환된다.

스택영역의 메모리 사용에 대해서 소스로 알아보자.

 public class StackMemory {

	public static void main(String[] args) {
		System.out.println("main(String[] args) 메소드 실행 시작 - stack 쌓임(push)");
		int sum = 0;
		int v1 = 10;
		int v2 = 20;
		System.out.println("main(String[] args) 메소드 영역 지역 변수 할당 및 초기화 (v1 = 10, v2 = 20, sum = 0)");
		sum = firstMethod(v1,v2);
		System.out.println("main(String[] args) 메소드 실행 종료 - memory 반환(pop)");
	}
	static int firstMethod(int a, int b){
		System.out.println("firstMethod() 메소드 실행 - stack 쌓임(push)");
		System.out.println("firstMethod() 메소드 영역 지역 변수 할당 및 초기화 (a = 10, b = 20)");
		secondMethod();
		System.out.println("firstMethod() 메소드 실행 종료 - memory 반환(pop)");
		return a + b;
	}
	static void secondMethod(){
		System.out.println("secondMethod() 메소드 실행 - stack 쌓임(push)");
		System.out.println("secondMethod() 메소드 실행 종료 - memory 반환(pop)");
	}

 }

stack 구조는 쌓는 방식으로써 순차적으로 실행을 할떄 main()프레임 영역이 쌓이고, 변수 선언 및 초기화를 하였을시에 main()영역 안에서 초기화가 이루어 진다. 

그러다가 firstMethod()가 실행이 되면 main()프레임의 작업을 잠시 맘추고 secondMethod()를 호출을 하여 stack에 main() 프레임 위에 쌓이게된다. 작업이 완료가 되면 메모리를 반환을 하고(pop)모든 호출을 통한 처리가 끝나게 된다.

Posted by SH후니
,