자바 소스 코드 시리즈 2 - HashMap의

HashMap소스가 많고 복잡하다, 이것은 코드 분석의 일반적으로 사용되는 부품의 단순한 제거하는 것이다. 제한 용량은 저를 수정하시기 바랍니다.

HASH 계산치

사전 지식 - 비트 컴퓨팅

비트 단위 배타적 OR 연산자 ^: 1 = 0 ^ 1, 0 = 0 ^ 0 ^ 0 1 = 0, 동일한 값이 0 인 경우, 값 1이 다르다. 비트 단위 XOR은 이진 XOR 연산의 모든 사람을위한 것입니다.

   1111 0000 1111 1110
^  1111 1111 0000 1111
______________________
   0000 1111 1111 0001
复制代码

제로 패딩은 오른쪽 시프트 연산자 비트 >>>: RIGHT 제로로 인한 갭을 채우도록 움직이고, 오른쪽 왼쪽 피연산자 피연산자 자리수 지정.

        1110 1101 1001 1111
>>> 4  
___________________________
        0000 1110 1101 1001
复制代码

섭동 기능

왜 방해합니까?

이론적 해쉬 값은 인 int직접 나하 사이 첨자 값 이렇게하면 타입 -2147483648 2147483648에서 표 2 INT 32 비트 부호 이진 고려 값의 범위를 고려. 전면 및 후면 매핑 공간에서 억 약 40까지 추가 할 수 있습니다. 해시 값을 직접 사용할 수 없도록 이러한 대형 어레이는, 메모리는, 입금 미만이다. 또한 배열의 길이는 사용하기 전에 처음으로 모듈로 연산을 수행 얻은 나머지 액세스 어레이 인덱스를 사용한다.

마지막 몇을 가지고 있기 때문에, 그래서 크게 해시 충돌의 가능성을 증가, 이번에는 방해 함수의 값이왔다.

섭동 계산

먼저 호출 hashCode()후, 외란 조작 방법 유도 해시 값.

16 비트 우측 시프트는 정확히 절반 (32 int32)의 상반부와 하반부 영역 XOR은이다 저차 확률을 증가시키기 위해, 하이 및 로우 레벨의 혼합 원래 해시 코드 섹스 . 혼합은 낮은 형상의 상부를 도핑 한 후, 이러한 정보는 또한 위장 높은 보존 할 수있다.

static final int hash(Object key) {
  int h;
  return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
复制代码

모듈로 지수를 계산

목표 시간의 계산되므로 해시 값리스트의 길이는 모듈로 연산을 수행 할 정도로 그 N- 대한 목록의 범위 내에서 산출 된 해시 값, list길이

i = (n - 1) & hash
复制代码

HashMap배열의 길이는 2의 전체 전력을 촬영합니다

이 때문에 (배열 길이 - 1)으로는 정확하게 대응하는 " 낮은 마스크 ." &결과는 인덱싱 된 액세스 배열에 사용되는 전용 저 가치를두고 제로 전체 동작의 높은 해시 값이다. (16) 초기 길이로, 예를 들어, 16-1 = 15,2 0,000 1,111 진수이다. 해시 값을 확인하고 &다음과 같이 작동합니다 :

    1010 0011 0110 1111 0101
&   0000 0000 0000 0000 1111
____________________________
    0000 0000 0000 0000 0101
复制代码

보증금은 무엇입니까 table

HashMap저장된 table값은 값 만이 아니라, 테이블에 저장된 노드 오브젝트 인스턴스로 구성된다.

(때 해시 충돌 연결리스트) 키, 값, 다음 해시 : 노드가 객체

이론적 최대 용량

int MAXIMUM_CAPACITY = 1 << 30;

전력 2 내지 30

하중 계수

부하율 부하 용량 (노드 수용 될 수있는 최대 수), 현재의리스트의 길이의 길이, 부하율 loadFactor를 계산하는 데 사용

부하 용량은 다음과 같이 계산된다 :

threshold = length * loadFactor

디폴트의 부하 계수는 0.75이다. 현재리스트의 길이의 75 %의 노드 수가 확장이 수행 될 때, 즉, 그렇지 않으면 해시 충돌의 가능성을 증가시킬 것이다. 하중 계수는 공간과 시간 효율성의 균형의 역할이다.

float DEFAULT_LOAD_FACTOR = 0.75f

확장 수행 할 작업

  1. 새로운 빈 어레이를 생성하는 항목은, 길이가 2 회 원의 배열이다. 로드는 확장 노드 용량의 수를 초과 할 경우.

old << 1 왼쪽 일 이전 * 2에 해당합니다.

  1. 다시 해시

    엔트리는 원래 배열 새로운 어레이에 대한 모든 재 해시 엔트리를 통과.

    왜 다시 해시? 확장 길이 후, 해시 값도 변경되기 때문에 (배열 길이 배열 첨자는 해시 모듈로 계산 됨).

    그래서 원래 해시 충돌 플레어를 나열 할 수 있음을, 배열은 드문 드문된다.

final Node<K,V>[] resize() {
    // 保存现有的数组
    Node<K,V>[] oldTab = table;
    // 保存现有的数组长度
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    // 保存现有的负载容量
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        // 如果现有容量已经超过最大值了,就没办扩容了,只好随你碰撞了
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        // 原有容量左移一位,相当于 oldCap * 2
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                    oldCap >= DEFAULT_INITIAL_CAPACITY)
            // 负载容量也扩大一倍
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    // 负载容量为0,根据数组大小和负载因子计算出来
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                    (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    // 遍历数组中所有元素,重新进行hash
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                // 删除旧索引位置的值
                oldTab[j] = null;
                if (e.next == null)
                    // 给新的索引位置赋值
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // 优化链表
                    // 把原有链表拆成两个链表
                    // 链表1存放在低位(原索引位置)
                    Node<K,V> loHead = null, loTail = null;
                    // 链表2存放在高位(原索引 + 旧数组长度)
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        // 链表1
                        // 这个位运算的原理可以参考第三篇参考资料
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        // 链表2
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    // 链表1存放于原索引位置
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    // 链表2存放原索引加上旧数组长度的偏移量
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}
复制代码

트리 변환

목록이 너무 오래, 그것은 레드 - 블랙 트리로 변환 될 것입니다.

리스트의 길이를 초과 할 경우 MIN_TREEIFY_CAPACITY나무의 최대 액 트리가 변화 될 것이다.

final void treeifyBin(Node<K,V>[] tab, int hash) {
  int n, index; Node<K,V> e;
  if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
    resize();
  else if ((e = tab[index = (n - 1) & hash]) != null) {
    ...
  }
}
复制代码

왜 나무?

보안 문제 ** 자연입니다. ** 쿼리 성능에 미치는 영향이 목록을 연결하기 때문에 누군가가 악의적으로 해시 충돌을 일으키는 경우, 그것은 서비스 공격의 해시 충돌 거부를 구성하는 것, 서버 CPU가 느린 서비스 나 사용할 수없는 결과 목록 쿼리를 많이하는 데 사용됩니다.

소스 시리즈

자바 소스 코드 시리즈 1 - ArrayList를

자바 소스 코드 시리즈 2 - HashMap의

자바 소스 코드 시리즈 3 -의 LinkedHashMap

참고

새로운 이해의 HashMap의 자바는 8 시리즈

HashMap의 JDK 소스 코드는 원칙 해시 방법은 무엇입니까? 지방 6 월 대답

상세한 HashMap의 소스 코드 분석 (JDK1.8)

이 문서는 먼저 내 개인 블로그에 출연 chaohang.top

저자 장 슈퍼

소스를 표시하시기 바랍니다

나는, 비행 업데이트에게 처음을받지 않습니다 마이크로 채널 대중 번호 [울트라]에 관심을 환영합니다.

추천

출처juejin.im/post/5e5273fde51d4526d87c6de4