http://hyeonstorage.tistory.com/265
여기 블로그 보고 작성했습니다. 해쉬 이해하기 쉽게 잘 쓰셨던데 지금은 휴면이라서.. 아쉽더군요.
Hash.java
public class Hash {
// 데이터 저장할 Entry는 값과 다음 Entry를 가지고 있는다.
// 해쉬테이블에 저장인덱스가 중복될 경우(충돌발생!) 분리연결법을 사용해 그 인덱스에 연결리스트를 만들어 관리하기 위한것.
private class Entry{
private int value;
public Entry next;
}
private int size;
private Entry[] hTable;
// 생성자.. size를 받아서 배열 테이블을 생성한다.
public Hash(int size) {
this.size = size;
this.hTable = new Entry[size];
}
// key 값으로 해쉬 테이블에 저장될 인덱스를 리턴
public int hashMethod(int key) {
return key % size; // 키값을 사이즈로 나눈 나머지 값으로 인덱스를 만든다. 사이즈가 10인 경우 키가 5라면 5, 12라면 2를 반환..
// 데이터가 많다면 사이즈를 크게 하는게 중복을 줄이는 길이겠지... 소숫값으로 하는게 더 나은 방법이다. 소수가 아닐때보다 중복값이 적음~
}
// 저장할 데이터로 키(해쉬코드)를 추출한다. 실제 해쉬에는 특별한 알고리즘이 적용돼 해쉬코드를 얻는다.
public int getKey(int data) {
return data; // 여기선 그냥 숫자값 그대로 키값으로 사용한다.
}
// 데이터를 해쉬테이블에 저장한다.
public int add(int data) {
// data의 키(해쉬코드)와 저장할 인덱스를 얻는다.
int key = getKey(data);
int hashValue = hashMethod(key);
// 저장할 Entry 생성
Entry entry = new Entry();
entry.value = data;
// 해쉬 테이블에 저장할 인덱스가 비어있다면, 저장하고 끝낸다.
if(hTable[hashValue] == null) {
hTable[hashValue] = entry;
return 0;
}
// 만약에 저장할 인덱스에 데이터가 있다면! 중복이므로, 엔트리 배열에 담아야한다.
Entry pre = null;
Entry cur = hTable[hashValue]; // 기존에 담겨있던 엔트리를 커서로 지정.
// 해쉬코드순으로 정렬한다는걸 기억하자.
// 해당 인덱스의 연결리스트(엔트리 배열)는 값의 크기가 작은 값부터 큰 순서로 정렬되어있다.
while(true) {
if(cur == null) {
System.out.println("cursor가 null입니다. pre.next를 들어올값("+ key +")로 설정합니다.");
//hTable[hashValue] = entry; // 이부분을 아래처럼 수정했더니 제대로 돌아감!
pre.next = entry;
return 0;
}else if(getKey(cur.value) < key) {
System.out.println("cursor 값("+ getKey(cur.value) +")이 들어올 값("+ key +")보다 작습니다. pre는 현재커서가 되고, 커서는 다음 커서로 넘어갑니다.");
// 해당 인덱스의 커서 노드의 해쉬코드값이 key(새로 들어올 데이터의 해쉬코드값)보다 작으면 커서를 하나 뒤로 옮긴다.
// 기존에 저장된 값이, 새로 들어올 값보다 작으면
// 기존 저장된 엔트리가 pre가 되고, 커서를 다음 노드로 옮긴다...
// (다음 노드가 null이면... 다음 while문에서 위에 조건문을 타면서 엔트리를 저장하고 끝.)
// 근데 여기서 드는 의문은 pre의 next에는 왜 아무것도 저장을 저장을 안하지..?? 계속 null 아닌가? 로직이 뭔가 이상한데...
pre = cur;
cur = cur.next;
}else {
// 해당 인덱스의 커서 노드의 해쉬코드값이 key(새로 들어올 데이터의 해쉬코드값)보다 크면, 여기 저장!!
// 해당 인덱스의 커서 노드의 값이 key보다 크면, 여기에 저장한다.
// 커서 노드가 HashTable의 첫번째 노드이면
if(cur == hTable[hashValue]) {
System.out.println("cursor 값("+ getKey(cur.value) +")이 리스트의 첫번째 값("+ hTable[hashValue].value +")과 같습니다.");
// 삽입 노드를 첫번째 노드로 삽입하고, 다음 노드를 커서노드로 지정한다.
entry.next = cur;
hTable[hashValue] = entry;
return 0;
}else {
System.out.println("cursor 값("+ getKey(cur.value) +")이 리스트의 첫번째 값("+ hTable[hashValue].value +")과 다릅니다. pre.next는 현재 들어올 값이 되고, 현재 들어올 값의 next는 커서가 됩니다.");
// 첫번째 노드가 아니면
// 삽입 노드의 다음 노드로 커서노드를 지정하고 전 노드의 다음 노들르 삽입노드로 지정한다.
entry.next = cur;
pre.next = entry;
return 0;
}
}
}
}
// 데이터가 있는 위치를 가져온다.
public int get(int data) {
int key = getKey(data); // 해쉬코드
int hashValue = hashMethod(key); // 인덱스
// 데이터가 있는 인덱스의 첫번째 노드를 얻는다.
Entry cur = hTable[hashValue];
System.out.println("cur===="+cur);
while(true) {
if(cur == null) {
// 인덱스가 비어있다면 -1 반환
return -1;
}else if(getKey(cur.value) == key) {
// 커서의 값과 찾으려는 데이터의 키가 같다면 해당 인덱스 반환
// 근데 이부분 이상한게 value랑 key를 비교하는게 맞나?
// getKey(cur.value) == key 이렇게 각 값의 해쉬코드를 비효해야하는거 아님? 지금이야 데이터 값이 키값이라서 이렇다곤 하지만..음..
return hashValue;
}else if(getKey(cur.value) > key) {
// 커서의 값이 찾으려는 키보다 크면 -1 반환
// 리스트는 작은 값에서 큰 값으로 정렬되어있다.
return -1;
}else {
// 커서의 값이 키보다 작으면 다음 노드로 커서 이동
cur = cur.next;
}
}
}
// 데이터가 있는 노드를 반환한다.
private Entry getEntry(int data) {
int key = getKey(data);
int hashValue = hashMethod(key);
// 해쉬테이블의 인덱스의 첫번째 노드와 두번째 노드
Entry pre = hTable[hashValue];
Entry cur = pre.next;
while(true) {
if(cur == null) {
// 커서 노드가 null이면 null 반환
return null;
}else if(getKey(cur.value) == key) {
// 커서 노드의 값이 키와 같으면 전 노드를 반환
return pre;
}else if(getKey(cur.value) > key) {
// 커서의 값이 키보다 크면 null 반환
return null;
}else {
// 커서의 값이 키보다 작으면 커서를 다음으로 이동
pre = cur;
cur = cur.next;
}
}
}
// data를 제거
public int remove(int data) {
Entry pre = null;
// 데이터가 있는 노드의 전노드를 가져오고 null이면 -1 반환
if((pre=getEntry(data))==null) {
return -1;
}
// 전 노드가 데이터의 다음노드를 가리키게 한다.
// 데이터 노드를 건너뛰게 연결한다(제거)
Entry cur = pre.next;
pre.next = cur.next;
return 0;
}
public String toString() {
StringBuffer result = new StringBuffer();
Entry cur = null;
for(int i=0; i<size; i++) {
result.append("[" + i + "]\t");
cur = hTable[i];
if(cur == null) {
result.append("n ");
}else {
while(cur!=null) {
result.append(cur.value + " ::: next=" + cur.next);
cur = cur.next;
}
}
result.append("\n");
}
return result.toString();
}
}
Test.java
public class Test {
public static void main(String[] args) {
//DecimalFormat f = new DecimalFormat("###,###.##");
//BigDecimal d = new BigDecimal(456745674.56);
//BigDecimal d = new BigDecimal(456745674);
//System.out.println(f.format(d));
Hash hash = new Hash(5);
System.out.println(hash.toString());
hash.add(2);
System.out.println(hash.toString());
hash.add(3);
System.out.println(hash.toString());
hash.add(10);
System.out.println(hash.toString());
hash.add(5);
System.out.println(hash.toString());
hash.add(10);
System.out.println(hash.toString());
hash.add(15);
System.out.println(hash.toString());
System.out.println("키가 5인 데이터를 가져오시오~~~"+hash.get(5));
System.out.println("키가 10인 데이터를 가져오시오~~~"+hash.get(10));
System.out.println("키가 3인 데이터를 가져오시오~~~"+hash.get(3));
System.out.println("키가 15인 데이터를 가져오시오~~~"+hash.get(15));
}
}
'Java' 카테고리의 다른 글
get resouce from classloader 주의할점 (0) | 2018.11.02 |
---|---|
자바 클래스패스 파일 load (0) | 2018.09.27 |
Spring fileupload maven (0) | 2017.12.06 |
소켓 프로그래밍 실습 (0) | 2017.11.28 |
프로그래밍은 참 어렵다. (0) | 2017.11.21 |