(재 인쇄) 왜 X % 길이 == X & (길이 -1) in HashMap (나머지 % 및 연산 및 변환 문제)

재판 : 원래 링크 https://blog.csdn.net/ricardo18/article/details/108846384
진술 :. 나는 다른 사람의 권리를 침해하는 경우, 나에게 연락하고 내가 그것을 삭제하겠습니다
오신 것을 환영합니다 전문가 나 스프레이

하나, 문제로 이어진다

HashMap의 소스 코드 구현을 설명 할 때 다음과 같은 사항이 있습니다.

① 초기 용량은 1 << 4, 즉 24 = 16
여기에 사진 설명 삽입
② 부하율은 0.75입니다. HashMap에 저장된 요소의 비율이 전체 용량의 75 %를 초과 할 경우 용량을 확장하고 초과하지 않을 경우 int 타입의 범위, 2의 거듭 제곱 (원본의 길이를 기준으로 2 배)의 확장을 수행하고
여기에 사진 설명 삽입
두 배로합니다
여기에 사진 설명 삽입
.③ 새로운 요소가 추가되면 HashMap에서이 요소의 위치가 계산됩니다. 이 기사의 주인공 해시 연산입니다. 세 단계로 나뉩니다.

첫 번째 단계 : hashCode 값 가져 오기 : key.hashCode ()

2 단계 : 높은 수준의 작업에 참여 : h >>> 16

세 번째 단계 : 모듈로 연산 : (n-1) & 해시

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
 
    tab[i = (n - 1) & hash];

추신 : 코드의 여섯 번째 줄이 직접 추가되었습니다.

좋은 해시 알고리즘은 요소의 분포를 더 고르게 만들어 해시 충돌을 줄일 수 있다는 것을 알고 있습니다. 이 영역에서 HashMap의 처리는 매우 영리합니다.

첫 번째 단계는 hashCode를 가져 오는 것입니다.이 메서드는 native로 장식 된 네이티브 메서드로 int 유형 값 (메모리 주소에 따라 변환 된 값)을 반환합니다. 일반적으로이 메서드를 다시 작성합니다.

두 번째 단계에서 획득 한 해시 값은 오른쪽에 16 비트 부호가없고 상위 비트는 0으로 채워집니다. 그리고 이전 단계에서 얻은 해시 코드로 비트 단위 XOR 연산을 수행합니다. 이것의 용도는 무엇입니까? 이것은 실제로 해시 코드의 충돌을 줄이기위한 방해 함수입니다. 오른쪽 시프트는 16 비트로 32 비트의 정확히 절반이고, 상위 절반 영역과 하위 절반 영역은 XOR 처리되어 원본 해시 코드의 상위 비트와 하위 비트를 혼합하여 하위 비트의 임의성을 높입니다. 또한, 혼합 된 저수준 특징은 일부 고수준 특징으로 도핑되어 고수준 정보도 위장 상태로 보존됩니다. 이는 높은 비트와 낮은 비트가 모두 해시 계산에 포함되도록하기위한 것입니다.

관심이 있으시다면 JDK1.7을 살펴보실 수 있습니다. 실은 4 번의 방해를했고 JDK1.8에서는 단 1 번만했습니다. 효율성을 확보하면서 갈등을 줄이기 위해서라고 생각합니다.
  여기에 사진 설명 삽입
이 기사의 초점은 세 번째 단계, 이전 두 단계를 통해 얻은 해시 값 및 비트 AND 연산의 경우 HashMap에서 1을 뺀 값인 (n-1) & hash입니다. 그러나 실제로 많은 해시 알고리즘은 요소 분포를 균일하게 만들기 위해 값을 사용하여 총 길이, 즉 n % hash를 변조하는 모듈로 연산을 사용합니다. 우리는 컴퓨터에서 &의 효율성이 %보다 훨씬 높다는 것을 알고 있으므로 %를 & 작동으로 변환하는 방법은 무엇입니까? HashMap에서는 (n-1) & hash가 계산에 사용되는데 왜 그렇습니까?

이것이 우리가이 블로그에서 이해할 질문입니다.

2. 결론

먼저 결론을 내립니다.

当 lenth = 2n 时 , X % 길이 = X & (길이-1)

즉, 길이가 2의 n 제곱 일 때 모듈로 % 연산은 비트 AND 연산으로 변환 될 수 있습니다.

예 : 9 % 4 = 1, 9는 이진수로 1001, 4-1 = 3, 3은 이진수로 0011입니다. 9 & 3 = 1001 & 0011 = 0001 = 1

또 다른 예 : 12 % 8 = 4, 12의 이진 값은 1100, 8-1 = 7, 7의 이진 값은 0111입니다. 12 & 7 = 1100 & 0111 = 0100 = 4

위의 두 예 4와 8은 모두 2의 n 제곱이고 결론은 사실입니다. 길이가 2의 n 제곱이 아닌 경우는 어떻습니까?

예 : 9 % 5 = 4, 9는 이진수로 1001, 5-1 = 4, 4는 0100입니다. 9 & 4 = 1001 & 0100 = 0000 = 0. 분명히 그것은 사실이 아닙니다.

왜 이래? 아래에서 자세히 분석해 보겠습니다.

3. 분석 과정

먼저 다음 규칙을 알아야합니다.

①, "<<"왼쪽 시프트 : 오른쪽의 비어있는 비트에 0을 더하고 왼쪽 비트는 단어의 시작 부분에서 압착되고 왼쪽 시프트는 1 비트 씩 2를 곱하는 것과 같습니다.

②, ">>"오른쪽으로 시프트 : 오른쪽 비트가 압착되고 오른쪽으로 1 비트 시프트 된 값은 2로 나눈 것과 같습니다. 왼쪽으로 이동 한 공간은 양수이면 0으로, 음수이면 사용하는 컴퓨터 시스템에 따라 0 또는 1로 채워질 수 있습니다.

③, ">>>"부호없는 오른쪽으로 이동하면 오른쪽 비트가 압착되고 왼쪽으로 이동 한 공간에 0이 추가됩니다.

이진수의 특성상 누구나 잘 이해한다고 생각합니다.

임의의 10 진수 XnXn-1Xn-2 ... X1X0이 주어지면 이진수로 분해합니다.

XnXn-1Xn-2… X1X0 = Xn 2n + Xn-1 2n-1 +… + X1 21 + X0 20 3-1 公式

여기서 십진수는 3 자리 만 가지고 있는데, 마찬가지로 N 자리가 있으면 2의 거듭 제곱이 0에서 N으로 차례로 증가합니다.

위의 결론으로 ​​돌아 가기 : lenth = 2n, X % 길이 = X & (길이 -1)

그리고 나눗셈의 경우 배당금이 배당률을 충족합니다 (제수는 충족되지 않음).

설립 : (a + b) ÷ c = a ÷ c + b ÷ c 3-2 공식

사실이 아님 : a ÷ (b + c) ≠ a ÷ c + b ÷ c

3-1 공식과 3-2 공식을 통해 10 진수를 2k 숫자로 나눌 때 10 진수를 3-1 공식의 표현으로 변환 할 수 있음을 알 수 있습니다.

(XnXn-1Xn-2… X1X0) / 2k = (Xn 2n + Xn-1 2n-1 +… + X1 21 + X0 20) / 2k = Xn 2n / 2k + Xn-1 2n-1 / 2k +… + X1 21 / 2k + X0 20 / 2k

위 공식의 나머지 부분을 찾으려면 한 눈에 알 수 있다고 생각합니다.

①. 0 <= k <= n 일 때 나머지는 Xk 2k + Xk-1 2k-1 +… + X1 21 + X0 20, 즉 k보다 큰 n 번째 거듭 제곱을 버립니다. 그들 모두는 2k로 나눌 수 있으며 우리는 모두 남겨집니다 (k보다 작 으면 2k로 나눌 수 없습니다). 그러면 나머지는 나머지입니다.

② k> n 일 때 나머지는 십진수 전체입니다.

이것을보고 우리는 결론을 증명하는 데 매우 가깝습니다. 위에서 언급 한 이진 시프트 연산으로 돌아가서 오른쪽으로 n 비트로 시프트한다는 것은 2n의 거듭 제곱으로 나누는 것을 의미합니다. 이로부터 매우 중요한 결론을 얻습니다.

10 진수는 2n 숫자의 나머지를 취합니다.이 10 진수를 2 진수로 변환하고 2 진수를 n 자리만큼 오른쪽으로 이동할 수 있습니다. 제거 된 n 자리가 나머지입니다.

나머지를 계산하는 방법을 알고 있는데, n을 제거하는 방법은 무엇입니까?

20, 21, 22 ... 2n을 이진수로 다음과 같이 보겠습니다.

0001,0010,0100,1000,10000…

위의 숫자를 하나씩 줄입니다.

0000,0001,0011,0111,01111…

AND 연산자 &의 규칙에 따라 비트가 모두 1이면 결과는 1이고, 그렇지 않으면 0입니다. 따라서 이진수가 2k의 나머지를 취하면 나머지가 유지 되더라도이 이진수와 (2k-1) 사이에 비트 AND 연산을 수행 할 수 있습니다.

이것은 이전에 주어진 결론을 완벽하게 증명합니다.

当 lenth = 2n 时 , X % 길이 = X & (길이-1)

위 공식을 만족하려면 2n이어야합니다. 그렇지 않으면 잘못된 것입니다.

요약하자면

위의 분석 과정을 통해 공식의 정확성을 완벽하게 증명했습니다. HashMap의 구현 프로세스로 돌아가서 HashMap의 초기 용량이 1 << 4이고 각 확장이 두 배인 이유를 알고 있습니다. 해시 알고리즘이 완벽하게 충족되어야하기 때문입니다.

추천

출처blog.csdn.net/qq_45531729/article/details/112370306