123

Java String의 hashCode 값은 (String.hashCode ()) :

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

다음 표현식이 거짓으로 평가되는 상황 (예 : JVM 버전, 공급 업체 등)이 있습니까?

boolean expression = "This is a Java string".hashCode() == 586653468

업데이트 #1 :대답이 "예, 그런 상황이 있습니다"라고 주장하는 경우 "This is a Java string".hashCode ()! = 586653468의 구체적인 예를 들어주십시오. 가능한 구체적 / 구체적으로 작성하십시오.

업데이트 #2 :우리는 hashCode ()의 구현 세부 사항에 의존하는 것이 일반적으로 나쁘다는 것을 압니다. 그러나, 나는 String.hashCode ()에 대해 구체적으로 말하고있다. 그래서 String.hashCode ()에 초점을 맞춘다. Object.hashCode ()는이 질문에서 전혀 관련이 없습니다.


  • 이 기능이 실제로 필요합니까? 왜 정확한 가치가 필요한가요? - Brian Agnew
  • @ 브라이언 : String.hashCode () 계약을 이해하려고합니다. - knorv
  • @Knorv 그것이 작동하는 방식을 정확하게 이해하는 것은 중요하지 않습니다. 계약과 그 우월한 의미를 이해하는 것이 더 중요합니다. - mP.
  • @mP : 귀하의 의견을 보내 주셔서 감사합니다. 그러나 결정할 때까지 결정하겠습니다. - knorv
  • 왜 그들은 첫 번째 인물에게 가장 큰 힘을 주었습니까? 여분의 계산을 보존하기 위해 속도를 최적화하려는 경우 이전 계산의 성능을 저장하지만 이전 계산은 마지막 특성에서 첫 번째 특성까지의 성능을 저장합니다. 이것은 또한 캐시 미스 (cache miss)가 있다는 것을 의미합니다. s [0] + s [1] * 31 + s [2] * 31 ^ 2 + ... + s [n-1] * 31 ^ [n의 알고리즘을 사용하는 것이 더 효율적이지 않습니다. -1] ? - android developer

7 답변


91

Java 1.2만큼 먼 저 설명서를 볼 수 있습니다.

그것은 사실이지만일반적으로당신은 해시 코드 구현을 그대로 유지해서는 안되며, 현재 문서화 된 동작이다.java.lang.String따라서 기존 계약을 위반하는 것으로 변경 될 수 있습니다.

가능하다면 버전 등에서 동일하게 머무르는 해시 코드에 의존해서는 안됩니다.하지만 내 마음에는java.lang.String알고리즘이 단순하기 때문에 특별한 경우입니다.있다지정된 알고리즘 ... 알고리즘이 지정되기 전에 릴리스와의 호환성을 포기할 의향이있는 한.


  • 문서화 된 String의 동작은 Java 1.2부터 지정되었습니다. API의 v1.1에서는 해시 코드 계산이 String 클래스에 지정되어 있지 않습니다. - Martin OConnor
  • 이 경우 자체 해싱 코드와 동료를 더 잘 작성합니다. - Felype
  • @ Felype : 나는 당신이 여기에서 말하고자하는 것을 정말로 모르지만, 나는 두려워합니다. - Jon Skeet
  • @ 존 스케이트 (JonSkeet)이 경우 우리는 이식성을 부여하기 위해 우리 자신의 해시를 생성하기 위해 우리 자신의 코드를 작성할 수 있습니다. 그렇지? - Felype
  • @ Felype : 당신이 말하는 이식성이나 "이 경우에는"어떤 의미인지는 분명하지 않습니다. - 어떤 특정한 시나리오에서? 나는 새로운 질문을해야한다고 생각합니다. - Jon Skeet

18

JDK 1.0 및 1.1 및> = 1.2에 대해 뭔가를 발견했습니다.

JDK 1.0.x 및 1.1.x에서는, hashCode   긴 Strings가 작동하는 함수   매 n 번째 문자를 샘플링합니다. 이   꽤 잘 당신이 가질 것이라고 보장   같은 문자열에 해싱하는 많은 문자열   가치, 따라서 Hashtable 속도가 느려집니다.   조회. JDK 1.2에서이 함수는   결과가 배가되었다.   지금까지 31 개를 추가하여 다음   문자. 이것은   조금 느리지 만 훨씬 낫다.   충돌을 피하십시오.   출처:http://mindprod.com/jgloss/hashcode.html

숫자가 필요하기 때문에 다른 점이 있습니다 : 해시 코드 대신 CRC32 또는 MD5를 사용하는 것이 어떻습니까? 토론이나 걱정할 필요가 없습니다.


10

특정 값과 동일한 해시 코드에 의존해서는 안됩니다. 같은 실행 내에서 일관된 결과를 반환합니다. API 문서는 다음과 같이 말합니다.

hashCode의 일반 규약은 다음과 같습니다.

  • Java 어플리케이션의 실행 중에 같은 오브젝트로 2 회 이상 불려 갈 때마다 hashCode 메소드는, 오브젝트의 equals 비교로 사용 된 정보가 변경되지 않으면, 같은 정수를 일관되게 돌려 줄 필요가 있습니다. 이 정수는 응용 프로그램의 한 실행에서 동일한 응용 프로그램의 다른 실행으로 일관성을 유지할 필요가 없습니다.

편집하다String.hashCode ()의 javadoc에서는, String의 해시 코드의 계산 방법을 지정하고 있기 (위해) 때문에,이 위반은 public API 사양을 위반하게됩니다.


  • 귀하의 답변은 유효 하나 특정 질문에 대해서는 답변하지 않습니다. - knorv
  • 그게 바로일반해시 코드 계약 -하지만특유한String 계약은 알고리즘의 세부 사항을 제공하고이 일반 계약 IMO를 효과적으로 대체합니다. - Jon Skeet

4

위에서 말했듯이, 일반적으로 클래스의 해시 코드에 의존해서는 안됩니다. 이후의 실행에서도동일한 신청~에같은 VM다른 해시 값을 생성 할 수 있습니다. AFAIK Sun JVM의 해시 함수는 모든 실행에서 동일한 해시를 계산하지만 보장되지는 않습니다.

이것은 이론적이지 않다는 것을 유의하십시오. java.lang.String의 해시 함수변경되었습니다.JDK1.2에서 (오래된 해시는 URL이나 파일 이름과 같은 계층 적 문자열에 문제가있었습니다. 끝에 만 다른 문자열에 대해 동일한 해시를 생성하는 경향이있었습니다).

java.lang.String은 특별한 경우입니다. hashCode ()의 알고리즘이 (지금) 문서화되었으므로 아마도 이에 의존 할 수 있습니다. 나는 여전히 나쁜 습관이라고 생각할 것이다. 특수하고 문서화 된 속성을 가진 해쉬 알고리즘이 필요하다면 하나만 쓰면됩니다 :-).


  • 그러나 JDK 1.2 이전의 문서에서 알고리즘이 지정 되었습니까? 그렇지 않은 경우 상황이 다릅니다. 알고리즘은 이제 문서에 정리되어 있으므로 공개 계약을 변경하는 것이 중요합니다. - Jon Skeet
  • (나는 그것을 1.1로 기억한다.) 원래의 (더 가난한) 알고리즘이 문서화되었다. 틀리게. 문서화 된 알고리즘은 실제로 ArrayIndexOutOfBoundsException을 발생시킵니다. - Tom Hawtin - tackline
  • @ 존 Skeet : 아, String.hashCode ()의 알고리즘이 문서화되지 않았다는 것을 알지 못했습니다. 물론 사물을 변화시킵니다. 내 의견이 업데이트되었습니다. - sleske

3

걱정할 또 다른 (!) 문제는 Java의 초기 / 후기 버전 간의 구현 변경 가능성입니다. 구현 세부 사항이 돌로 설정되어 있다고 생각하지 않으므로 잠재적으로미래Java 버전으로 인해 문제가 발생할 수 있습니다.

최종선은, 나는 의지를 의지하지 않을 것이다hashCode().

아마도이 메커니즘을 사용하여 실제로 어떤 문제를 해결하려고하는지 강조 할 수 있으며 이는보다 적절한 접근 방법을 강조합니다.


  • 귀하의 답변에 감사드립니다. " 자바 문자열입니다. " .hashCode ()! = 586653468의 구체적인 예를 들려 줄 수 있습니까? - knorv
  • 아뇨. 미안 해요. 내 요점은 당신이 테스트 한 모든 것이 당신이 원하는대로 작동 할 수 있다는 것입니다. 하지만 여전히 보장 할 수는 없습니다. 그래서 만약 youre ' 당신이 VM 등을 제어 할 수있는 (말하자면) 단기 프로젝트에서 일한다면, 위가 당신을 위해 일할 수도 있습니다. 하지만 더 넓은 세상에서 그 기술에 의존 할 수는 없습니다. - Brian Agnew
  • " 향후 Java 버전으로 업그레이드하면 문제가 발생할 수 있습니다. " 향후 Java 버전으로 업그레이드하면 hashCode 메소드가 완전히 제거 될 수 있습니다. 또는 문자열에 항상 0을 반환합니다. 나중에 호환되지 않는 변경 사항입니다. 문제는 Sun ^ HOracle ^ JCP가 그것을 깨뜨리는 변화라고 생각할 것이므로 피할 가치가 있는지 여부입니다. 이 알고리즘은 계약서에 포함되어 있기 때문에 희망을 갖습니다. - Steve Jessop
  • @SteveJessop 음, 이후switch특정 고정 해시 코드에 의존하는 코드를 컴파일하는 문자열에 대한 명령문.String해시 코드 알고리즘이 기존 코드를 확실히 깨뜨릴 것입니다 ... - Holger

3

질문에 답하고 토론을 계속하지 말라. 아파치 하모니 JDK 구현은 다른 알고리즘을 사용하는 것처럼 보입니다. 적어도 완전히 다른 것처럼 보입니다.

Sun JDK

public int hashCode() {
    int h = hash;
    if (h == 0) {
        int off = offset;
        char val[] = value;
        int len = count;

        for (int i = 0; i < len; i++) {
            h = 31*h + val[off++];
        }
        hash = h;
    }
    return h;
}

아파치 하모니

public int hashCode() {
    if (hashCode == 0) {
        int hash = 0, multiplier = 1;
        for (int i = offset + count - 1; i >= offset; i--) {
            hash += value[i] * multiplier;
            int shifted = multiplier << 5;
            multiplier = shifted - multiplier;
        }
        hashCode = hash;
    }
    return hashCode;
}

직접 확인해보십시오 ...


  • 나는 그들이 차갑고 멋지다고 생각합니다. :) "(승수 < < < < 5) - 승수 > 결국 31 배의 배율입니다. - unwind
  • 확인 : 동일한 결과 ... - Carlos Heuberger
  • 좋아, 너무 게으르다. 감사! - ReneS
  • 그러나 내 측면에서 명확하게하기 위해서 ... 해시 코드는 내부적 인 것이기 때문에 결코 해시 코드에 의존하지 마십시오. - ReneS
  • @androiddeveloper 이제 재미있는 질문입니다. 사용자 이름을 기반으로 추측 했어야했는데. 로부터Android 워드 프로세서계약서가 똑같은 것 같습니다.s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]내가 잘못 본 것이 아니라면, 안드로이드는 변경없이 Sun 객체의 String 객체 구현을 사용하기 때문입니다. - K_7

2

변경 사항과 호환되지 않는 VM이 걱정된다면 기존의 해시 코드 구현을 자신의 유틸리티 클래스에 복사하고이를 사용하여 해시 코드를 생성하십시오.


  • 나는 이것을 말할 것입니다. 다른 답변이 질문에 대답하는 동안 별도의 hashCode 함수를 작성하는 것이 아마도 knorv의 문제에 대한 적절한 해결책 일 것입니다. - Nick

연결된 질문


관련된 질문

최근 질문