네이버 카페 코드인에 심심해서 적어 놨던 글을 다시 옮겨옴.
( 워낙 포스팅이 없어서 ;; 요런걸로 때움. ㅋㅋㅋ )
-------
안녕하세요.
찬 입니다.
우리가 일반적으로 Map이나 HashTable을 쓸때 다음과 같이 사용하지요.
Map map = new HashMap(100);
map.put("찬", new Person( Person.MEN, 29 ) );
map.put("철수", new Person( Person.MEN, 15) );
map.put("영희", new Person( Person.WOMAN , 13 ) );
이때 map에
key로 "찬", "철수", "영희" 와 같이 String을 주고,
value로는 Person 객체를 만들어서 넣어 줍니다.
이때,
map에서는 key 값이 중복되면 기존에 있던 value에다가 새로운 value를 덮어 써 버리게 되는 것처럼 보입니다.
map.put("찬", new Person( Person.MEN, 29 ) );
map.put("철수", new Person( Person.MEN, 15) );
map.put("영희", new Person( Person.WOMAN , 13 ) );
map.put("철수", new Person( Person.WOMAN, 15 ) ) ; // 으악! 철수를 여자로 만들어 버렸어!!
put할때에는 key의 hashcode를 보고 쓰게 되어 있죠.
즉, "철수".hashCode() 값이 같기 때문에, 예전에 있던 값에다가 덮어써 버리게 됩니다.
( 정확하게 말하면, 원래의 Hash 개념으로는 hashCode가 같은 애들은 bucket에 저장됩니다. 덮어 써 버리지는 않습니다. 하지만 자바의 기본 구현에서는 Key의 hashCode에 맞춰서 하나만 들어 가요. )
그렇다면 String의 hashCode()에 대해서 좀 알아 봅시다.
아래는 String의 hashCode()에 대한 API문서 내용입니다.
hashCode
public int hashCode()- Returns a hashcode for this string. The hashcode for a
String
object is computed ass[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
usingint
arithmetic, wheres[i]
is the ith character of the string,n
is the length of the string, and^
indicates exponentiation. (The hash value of the empty string is zero.) -
- Returns:
- a hash code value for this object.
보다시피 String의 hashCode()는 "s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]" 요런 공식에 의해서
값이 반환되게 되어 있는데요, 공식이 무진장 어렵죠. 무슨 말인지도 잘 모르겠고..
공식보다는 hashCode()의 반환값에 대해서 이야기 해 보죠.
hashCode()의 반환값은 int입니다. String 객체의 hashCode()를 가지고 오면 int형태를 반환하게 되죠.
자, 그럼 다시 생각해 봅시다.
int는 총 4byte를 차지할 수 있는 정수형 기본 타입입니다.
4byte로 표현할 수 있는 갯수는 0부터 0xFFFFFFFF(4,294,967,295) 입니다.
즉, hashCode()의 결과는 그 많은 int값 중에서 하나를 반환됩니다.
그렇다면 또 다시 String의 hashCode를 생각해 봅시다.
하지만 String은 무한개를 생성해 낼 수 있습니다. ( "A", "A1",..."AZ", "AA1", "ZZ....ZZZ", "가1A" .... 등등 )
그런데, 이렇게 무한개로 생성해 낼 수 있는 String 객체의 hash code는 int가 반환할 수 있는 숫자중에 하나 입니다.
확률상 서로 다른 String 인데도, 같은 hashcode를 가지는 String이 있을 수 있습니다.
그렇기 때문에,
HashMap이나 Hashtable를 사용할때 아무런 생각없이 String을 key으로 주고 쓰고 있습니다.
재수가 없으면 데이터가 날아 가는 문제가 발생할 수 있습니다.
( 또 다시 이야기 하자면, 데이터가 날아 가지는 않습니다. - 자세한 사항은 HashMap의 코드를 분석해 보세요~ )
그러므로 정말로 반드시 유일한 값이 필요하다고 한다면, hashCode()를 사용해서는 안될것입니다.
char[]을 일일이 가지고 와서 적당히 조작해서 int값으로 만드는 방법을 쓰던지 해야 할것입니다.
( 하지만, 위에서도 말했다시피 - int는 제한적이고 string은 무제한이므로, int를 가지고 어찌할 방법은 없습니다. )
지민아빠님의 코멘트처럼, 사실은 hashCode가 같다고 해서 꼭 문제가 되는것은 아닙니다.
이미 들어 있는 hashCode를 가진 key를 이용해서 put하면, return으로 이전에 들어 있던 Object가 나오거든요.
String.hashCode()는 유일한 값을 반환하지 않는다는 것입니다.
그러므로 이글 오해할 가능성이 있는 내용을 포함하고 있습니다.
잘 이해가 되지 않으시는 분들은 이 게시물에 cobus 님께서 달아 두신 comment를 참고하시면 이해가 되실 겁니다. ^_^
오늘 또 어느 익명의 정의에 사도께서는 "이 딴 글은 내려버려라~" 고 적어 주셔서, 내용을 일부 수정합니다.
어흑. 진짜 글을 내려 버려야 하나.. 쩝ㅋㅋㅋ
'공부 > 컴퓨터' 카테고리의 다른 글
[Java/Tip] String.intern()은 메모리를 아낄 수 있다? (21) | 2008.11.25 |
---|---|
[Java/Tip] Hashtable을 제대로 활용하지 못하는 경우... (12) | 2008.11.20 |
[Java/Tip] String.hashCode()는 유일한 값을 반환할까? (17) | 2008.11.18 |
[Java/Tip] Specifc Debugging Tips for Swing - 4.2.1 Incorrect Threading (2) | 2008.11.05 |
Character에서 나오는 hanzi, kanji 는 뭥미? (2) | 2008.09.03 |
[책] 소프트웨어 컨플릭트 2.0 - 시대를 뛰어넘는 즐거운 논쟁 (4) | 2008.06.29 |
-
지민아빠 2008.11.19 00:40
hashcode 는 같은 값이 나오지만, 그래서 equals 도 같이 사용하는 거잖아요. 내부적으로는 그래서 예비 버킷이 있는.... 흠. 암튼. 유용한 글.. 좋아요. ㅎㅎ
-
쓩 2009.08.27 12:41
해쉬가 같으면 같은 빠겟스에 담는다는거고...
빠겟스 수는 적절하게 조절되어질테고~
결론이 좀....ㅋㅋㅋ
여튼 잘봤음 -
-
흠.. 2011.06.20 18:34
오해할 소지가 있는 글이 아니고
아예 개념을 잘 모르고 쓴 글이네요...
아직도 안지우고 냅두고 있다는 것이 신기합니다..
나는 바로 내릴 것 같은데.. -
지나가다 2013.08.29 01:55
해쉬코드는 문자열에 유일합니다.
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
위가 hashCode() 소스입니다.
value는 해싱할 문자열이고
각 글자자릿수의 지수승을 해서 각 문자의 ASCII코드를 더합니다.
abc 를 입력하면 96354이 나옵니다. -
cobus 2015.11.27 11:10
글 내용을 잘 이해를 못하신건지 아니면 .hashCode()를 맹신하시는건지...
본문에 String class의 hashCode() 를 사용했을 때 같은 값이 나오는 예제를 추가하면 논란이 없을 것 같습니다.
약간 수정되어야 할 부분은 hashCode() 값이 중복될 수 있는 이유를 int의 수와 String의 표현 가능 수가 차이가 나서 중복이 생길 수 있다고 하셨는데 사실은 ASCII code값을 이용해 비트연산의 합으로 hash값을 만들다보니 길이와 상관없이 Hash값 중복이 발생할 수 있습니다. 본문에 추가하면 좋을것 같은 hashCode() 중복 문자열은 아래와 같습니다.
String a = "Z@S.ME";
String b = "Z@RN.E";
if(a.hashCode() == b.hashCode()) {
System.out.println("same hashcode");
} else {
System.out.println("different hashcode");
}
결과: same hashcode-
cobus님 //
댓글 감사합니다. ^_^
저희 의도는 int의 표현 가능 갯수보다 String은 표현가능 갯수가 더 많기 때문에
String을 int로 mapping 시킨다면 당연히, int 값이 겹치는 경우가 발생할 수 밖에 없다라는 의미로 적었습니다. ( 원론적인건 코드가 필요 없으니까요 ^_^ )
제가 그 의미를 정확하게 표현하지 못했네요 ^_^
이 글을 읽으시는 분이 코멘트 남겨 주신 내용을 읽으시면,
훨씬 더 잘 이해할 수 있을것으로 보입니다. ^_^
자세한 설명과 구체적인 예를 들어 코멘트 남겨 주셔서 고맙습니다. ^_^
해당 글에, cobus 님의 코멘트를 확인하라는 내용을 추가 했습니다. ^_^
-
-
멍멍 2020.03.11 21:07
[해시코드가 문자열에 대한 고유값을 가질 수 있는가]를 명제로 넣을 수 없다는 결론까지 잘 나와있는 듯 한데, 중간에 구조가 틀렸다는 건지, 개념이 틀렸다는 건지를 이해 할 수가 없네요.
이 문제는 무한의 값을 가진 자료를 유한의 공간에 1:1로 연결 할 수 있는가? 라는 질문에 부합합니다.
문자열은 무한의 값에 해당하는 자료일 것이고 해시코드 그러니까 4byte짜리 정수공간이 되겠죠.
보통 1:1 연결을 할 때는 선형적 자료 변환이 가능 할 까? 에 대한 질문의 답이 나와야 하는데, n개 길이의 문자열은 256(1byte) ^ n의 갯수를 가질 수가 있습니다. 해시코드는 이미 256(1byte) ^ 4라는 공간을 고정적으로 사용하고 있네요. 결국 1:1매핑은 4글자 ascii코드에 한해서만 1:1 매핑이 가능하다는 뜻이 됩니다. 물론 뭐 사용하지 않는 문자열이나 이것저것 조건을 섬세하게 합치면 한 6~7글자 정도 까지는 가능 할 수도 있겠지만, 기본적으로는 그 정도가 1:1매핑의 한계점이라는 소리죠. 때문에 이러한 부분들을 고려해서 버켓이나 서브 인덱싱이나 뭐 이런 여러가지 방법이 있겠지만 원론적으로 불가능 하다는 겁니다.
얼마나 대단한 개념을 알고 있어야 이 질문에 대한 답이 틀린 것이 아니다 라는 답을 받게 될 지는 모르겠지만, 제가 보기에는 전혀 틀린게 없는 이야기네요. -
허허 2020.12.11 13:39
댓글보니
자기라면 글을 바로 내렸다라..
마에스트로 납셨네
잘못된부분이있다면 다른분들처럼 의견을 제시하지
개념을 모르고 쓴글이라
그런데 무슨개념을 몰랐는지는 말이없고
본인이 더 개념이 없는것 같은데 ㅉㅉ
글 잘 읽었습니다
저딴소리는 개념치마시길 바랍니다.
겁이 많은 개가 짖기만 하는 법입니다.
다소표현이 과격합니다 원하시면 얼마든지 제글 지우셔도됩니다 ㅎㅎ
아 그리고 하나 매우 불만이있는데요
글저장할때 그림문자 너무 알아보기 힘들어요 ㅎㅎㅎㅎㅎ