HashMap 소스 를 읽 고 쓴 노트 입 니 다.

9569 단어
본문 은 jdk 1.8 소스 코드 이다.1.7 와 가장 큰 차 이 는 붉 은 검 은 나 무 를 끌 어 들 인 것 이다.1.8 이전에 HashMap 의 데이터 구 조 는 [배열 - 링크] 1.8 이후 [배열 - 빨 간 검 은 나무] 를 추 가 했 습 니 다. 기본적으로 링크 가 8 보다 클 때 빨 간 검 은 나무 구조 로 바 뀌 었 습 니 다.
  • [기본 값] 초기 화 용량
    /**
    *      。16
    * The default initial capacity - MUST be a power of two.
    */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
     ```
    
  • [기본 값] 확장 시작 기본 값 은 0.75 의 용량 을 차지 할 때 확장 합 니 다. 즉 3 / 4 입 니 다.확장 수량 은 2 배 이 며, put () 방법 에 새로운 value 삽입 이 완료 되면 확장 여 부 를 판단 합 니 다
    /**
     *    0.75    ,  . 3/4。     2 
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
  • [기본 값] 링크 의 길이 가 8 을 초과 하면 빨간색 과 검은색 나무 로 바 뀌 고 6 보다 작 으 면 링크
    static final int TREEIFY_THRESHOLD = 8;
    static final int UNTREEIFY_THRESHOLD = 6;
    
  • 로 떨어진다.
  • [방법] 구조 적 방법 으로 사용자 정의 초기 용량 과 확장 방안 을 설정 합 니 다
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                    initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                    loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }
    
  • [방법] put (Key, Value) 에서 가장 자주 사용 하 는 방법 은 반환 값 이 있 습 니 다.키 가 존재 하 는 상황 에서 오래된 value 를 되 돌려 주 고 키 가 존재 하지 않 으 면 null 을 되 돌려 주 는 것 을 발견 할 수 있 습 니 다.put 방법 은 jdk 1.8 에서 가장 크게 바 뀌 었 고 puutVal () 방법 이 하나 더 생 겼 습 니 다.안에 빨간색 과 검은색 (TreeNode) 의 데이터 구 조 를 추 가 했 고 HashMap 의 조회 속도 가 향상 되 었 다
      public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
      }
    
    /**
     * Implements Map.put and related methods
     *
     * @param hash hash for key
     * @param key the key
     * @param value the value to put
     * @param onlyIfAbsent    true       key value
     * @param evict if false, the table is in creation mode.
     * @return previous value, or null if none
     */
      final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        /**
          *       HashMap     :tab    。    Node(      )
          */
        Node[] tab; Node p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        /**
          *  hash      index
          */
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node e; K k;
            if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            /**
             *   table[i]    treeNode, table[i]       ,
             *       ,           
             */
            else if (p instanceof TreeNode)
                e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
            else {
                /**
                 *   table[i],          TREEIFY_THRESHOLD(    8),  8           ,
                 *            ,           ;        key        value  ;
                 */
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        /**
                          *      Node next         Node,      
                          */
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
          /**
           *      , key            value
           */
            if (e != null) { // existing mapping for key
                /**
                *value      ,      ,      
                */
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        
       /**
         *      ,            size         threshold,
         *     ,    
         */
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    
  • [내부 류] Node 링크 와 빨 간 검 은 나무 구조.jdk 1.8 이전.이 실 체 는 Entry
    static class Node implements Map.Entry {
      final int hash;
      final K key;
      V value;
      /**
        *     Node   next ,       
        */
      Node next;
    
      Node(int hash, K key, V value, Node next) {
          this.hash = hash;
          this.key = key;
          this.value = value;
          this.next = next;
      }
    
      public final K getKey()        { return key; }
      public final V getValue()      { return value; }
      public final String toString() { return key + "=" + value; }
    
      public final int hashCode() {
          return Objects.hashCode(key) ^ Objects.hashCode(value);
      }
    
      public final V setValue(V newValue) {
          V oldValue = value;
          value = newValue;
          return oldValue;
      }
    
      public final boolean equals(Object o) {
          if (o == this)
              return true;
          if (o instanceof Map.Entry) {
              Map.Entry,?> e = (Map.Entry,?>)o;
              if (Objects.equals(key, e.getKey()) &&
                  Objects.equals(value, e.getValue()))
                  return true;
          }
          return false;
      }
    }
    
  • 라 고 합 니 다.
  • [방법] resize () 용량 조정 방법
    /**
     * Initializes or doubles table size.  If null, allocates in
     * accord with initial capacity target held in field threshold.
     * Otherwise, because we are using power-of-two expansion, the
     * elements from each bin must either stay at same index, or move
     * with a power of two offset in the new table.
     *
     * @return the table
     */
    final Node[] resize() {
        Node[] 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;
            }
          /**
           *    *2       ,          (8) ,
           *    ,   2 
           */
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                    oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold     2 
        }
        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);
        }
        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[] newTab = (Node[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    /**
                     *       ,          。
                     *  UNTREEIFY_THRESHOLD=6     6     
                     */
                    else if (e instanceof TreeNode)
                        ((TreeNode)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node loHead = null, loTail = null;
                        Node hiHead = null, hiTail = null;
                        Node next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
    
  • [방법] 옮 겨 다 니 는 효율 향상: entry Set () 방법 은 entry 집합 으로 돌아 갑 니 다. Map 을 옮 겨 다 닐 때 이 entry Set () 을 무시 합 니 다. 대부분 key Set () 방법 을 사 용 했 습 니 다. 이 집합 은 key 만 되 돌 아 왔 습 니 다. value 를 찾 으 려 면 get () 을 다시 호출 해 야 합 니 다. 이번 호출 은 자원 을 낭비 합 니 다
    public Set> entrySet() {
        Set> es;
        return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
      }
    
  • 좋은 웹페이지 즐겨찾기