HashMap source code analysis (the most detailed history of the source code analysis)

HashMap

HashMap development is most frequently used for mapping (key-on key value) processing the data structure

ObjectI entry <Key, Value>, entry <Key, Value>] may be in the form of data stored by up key

 

Usually see the database optimization, index optimization is specific, what is the index to optimize it?

For example, micro-channel contacts, az rightmost be sorted, which is the index, the data packet, and uses a data structure hashMap

 

We often hashMap data structure called a hash chains; 

 

Why set a key set of values ​​is a collection of value

public  abstract  class AbstractMap <K, V> the implements the Map <K, V> { 

the Set <K> keySet; 

Collection <V> valuescollection; 
SET data structures: the same elements can not
collection data structure: may be the same element
as in the hashMap, key- key can not be the same, value- the same value can be

 

 

hashMap maximum and minimum capacity

Private  static  Final  int MINIMUM_cAPACITY =. 4 ; // minimum capacity 

Private  static  Final  int MAXIAMM_CAPACITY <<. 1 = 30; // maximum capacity, fast approaching the maximum value of type int (x 2 left and right removed 2)

 

Key (when empty) the minimum capacity of the array

private static final Entry[] EMPTY_TABLE = new HashMapEntry[MINIMUM CARACITY >>>1];

 

When capacity reaches three-quarters of the time in the hashMap, ready expansion of the

static final float DEFAULT_LOAD_FACTOR=.75F;

 

An array of key-value pairs, with each store a key-value pair

transient HashMapEntry<k,V>[] table;

 

No key value pairs

transient HashMapEntry<K,V>entryForNullKey;

 

private transient int threshold;

private transient Set<K>keyset; 
private transient Set<Entry<K,V>>entrySet;
private transient Collection<V>values;

 

 

Want to understand the structure of the data source, a rationale clear later when a new element is added in, and the old elements will be produced before anything

First look at the relationship between inheritance and member variables to see, to see relationships between elements, the relationship between the elements to see is when adding elements, which groups elements and elements before there is anything to do, put methods

 

 

  What is HASH

  The numerical data is a content file obtained by the logical operation, different file HASH value (even for the same file name) obtained is different, so it would HASH value for each file in the identity card EMULE.

secondary: second

table: Array stored key-value pairs

tab.lenth = index of the maximum

e: tab [index]: The first element of a one-dimensional array, the entire list of the first node

 

@Override
 public V PUT (key K, V value) {
     IF (key == null ) {
         return putValueForNu1lKey (value); // put an empty key values Note: hashMap value is null 
  }
int the hash = the Collections . at the 2 secondaryHash (key); // get the hash value of the key is first performed after the key-entered twice hash: first to get the key.hashCode () hash value itself, then it is as a parameter (Object key ) passed in, is the second hash
                                                       Objective: to not have been in a key data field, the number to be dispersed, evenly arranged, in 0-9
under the next . 1 HashMapEntry
<K, V> [] Tab = Table; // to security declare local variables the Tab int index = hash & (tab.length - 1); // with the operation performed by calculating a hash value and then, where to obtain the index to be placed. Equivalent micro-channel index, calculated location, analogy, to --c, c in a region where a plurality of keys region may
                   need only two lines of code, to find the hash key (n) is located, for example linked list 10, 10 need to check before, now only need to check 1/10 times, 10 times the efficiency, then by iteration find the particular element, link lists 100 to 100 times more efficient
for (HashMapEntry <K, V> Tab = E [index]; E =! null ; E = e.next) {// traverse the entire subscript following the entire list, e: the head node, if the first node is not equal to null, the first node is equal to let him next node
     
IF (e.hash == hash && key.equals (e.key)) {in the key hash is equal to the calculated hash, and then found the same key and put to the linked list of the key, is overwritten
                 preModify ( E); covering the 
                V oldValue = e.Value; oldValue assigned to the previous value the new value 
                e.Value = value;
                 return oldValue; 
            } 
        }

      modCount ++; not found count + 1'd
      IF (size ++> threshold) {// the number of the size, i.e. size, if the size ++ factor greater than the capacity limit, to expand

        = Tab doubleCapacity (); capacity multiplied by 2, to triple. The minimum is 4,2 2 2 . 3 2 . 4 2 . 5 2 . 6 ...

        index = hash &(tab.1ength-1);扩完容再重新计算一遍下标的值

 

      }        

      addNewEntry(key, value, hash, index);

      return nul1;

 }

 

public static int ①secondaryHash(Object key){
  return ②secondaryHash(key. hashCode());//先获取key本身的hashcode,再经过一次hash,调用secondaryHash
}

 

private static int 上上2secondaryHash(int h) {
    h += (h << 15) ^ 0xffffcd7d;
    h ^= (h >>> 10);
    h += (h << 3);
    h ^= (h >>> 6);
    h += (h << 2) + (h << 14);
    return h ^ (h >>> 16);
}

 

hashMap不仅仅是一种单纯的一维数组,有键key,有值value,还有next指针,这样的好处是HashMap根据键的hashcode值存储数据,大多数情况可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序是不确定的

static class 上上上1HashMapEntry<K, V> implements Entry<K, V> {
    final K key;
    V value;
    final int hash;
    HashMapEntry<K, V> next; //如果出现相同的键,就通过这个指针指向下一个
    HashMapEntry(K key, V value, int hash, HashMapEntry<K, V> next) {
        this.key = key;
        this.value = value;
        this.hash = hash;
        this.next = next;
  }

 

oldCapacity:扩容之前的长度

newTable:

 

private HashMapEntry<K,V>[] doubleCapacity(){
  HashMapEntry<K,V>[] oldTable=table; //作为局部变量赋值
  int oldCapacity =oldTable.1engtth;  
  if(oldCapacity == MAXTMUM_CAPACITY){ //现在的长度就等于最大就不扩容了
    return oldTable; 
}   
int newCapacity = oldCapacity*2; //否则,扩容2倍   HashMapEntry<K,V>[] newTable = makeTable(newCapacity); //声明了一个newTable,让他的长度等于newCapacity if(size==0){ //没元素在里面     return newTable; }
  for(int j=Q;j<b1dcapacity;j++){ //遍历,为了把老数组里的元素放到新数组里面来

     HashMapEntry<K,V>e=oldTable[j]; //拿到老的数组里的每一个键值对

     if(e==nul1){
       continue;

     }键值对为空,则不管

 

    int highBit=e. hash & oldCapacity; //拿到键值对里的hash和oldCapacity长度,进行取小(highBit)

    HashMapEntry<K,V>broken=null;

    newTable[j | highBit]=e; //把highBit放在newTable里面,和j进行一个或运算,其实就是把元素丢到新的里面来

    for(HashMapEntry<K,V>n=e. next;n!=null;e=n,n=n. next){ //把串全部拿出来
      int nextHighBit=n. hash & oldCapacity;

      if(nextHighBit!=highBit){ //如果后面子串和前面这个串,计算出来的下标不同,不能再放在这个数组(相当于微信的一个索引)里了
        if(broken==null)  //不相等
          newTable[j | nextHighBit]=n;  //应该放在newTable新的下标去   或运算的时候分成两个区间

        else

          broken.next = n; //如果相等,放在next后面,继续串起来

           

        broken=e;

        heigBit = nextHighBit;

      }

    }

    if(broken !=null)

      broken. next=null;

      }

    return newTable;

  }

 

 

把串里面的数据重新拿出来了一遍,重新计算下标

串1以前在第一个,现在在第五个,帮它放在五的地方,如果也是在第一个地方,就作为next挂起来

 

 

table[index] = next  也就是链表中下一个元素

table[index]就相当于微信同一个索引下的某个元素,有两个了,再添加,就用next指向下一个元素

void addNewEntry(K key,V value,int hash,int index){
table[index]=new HashMapEntry<K,V>(key,value,hash,table[index]);
 ,这个图就是,tab[index]这个一维数组中的某个元素或存储区域,让它等于新加进来的元素--newHashMap,让新进来的元素的next指针指向tab[index]

 

 

第一排,只有最后4位才有效,因为与运算全是1才为1,所以 0000=1001=0(最小值)      1001+1001=9(最大值)         hash%10也是(0~9),因为hash不固定 

与运算lenth-1均匀的分布成0~9  或运算分成两个区间

 

hash相同   key不一定相同       >> key1 key2产生的hash很有可能是相同的,如果key真的相同,就不会存在散列链表了,散列链表是很多不同的键算出的hash值和index相同的

key相同  经过两次hash hash一定相同

 

 remove

tab[index]:头结点

prev=null 默认为空

 

@override
public V remove(object key) {
  if (key == null) {
    return removeNullke(); //移除空键但有值的键值对
  }
 int hash = Collections.secondaryHash(key);
  HashMapEntry<K, V>[] tab = table;
  int index = hash & (tab.length - 1);
  for (HashMapEntry<K, V> e = tab[index], prev = null; //遍历串里的每一个元素,让头结点等于e
      e != null;prev = e, e = e.next){ //让头结点e等于prev,又让e.next(头结点的下一个元素等于e)
           二次遍历
    if (e.hash == hash && key.equals(e.key)) { hash相等,又能equals,说明找到了这个结点
      if (prev == null) {
        tab[index] = e.next; 让头结点不再等于之前的prev,把e放在头结点位置,然后e.next就是tab[index](头结点),成功上位了哈哈

      } else {
        prev.next = e.next; prev.next不指向下一个元素了,指向下一个的下一个(e.next),就表示把e删除了
      modCount++;
      size++;

要删除一个key1,找到下标索引就是index=0 第一列,但是这个index=0有很多键值对,不能直接把index=0里的所有键值对删除吧,所以要先查找找出来,删除需要的键值对

 

public interface Map<K,V>{}

public static interface Entry<k,V){}

public boolean equals(Object object); 要重写,因为要相等的话,键和值都要相同

public K getkey();获取键

public V getValue(); 获取值

public V setValue(V object); 设置值

public void clear(); 清除这个map

public boolean containskey(Object key); 是否包含某个键

public boolean containsValue(Object value); 是否包含某个值

public Seft<Map. Entry<K,V>>entryset(); 获取实体集合

public Set<k>keyset(); 获取键的集合

public Set<k>Valueset(); 获取值的集合

public V put(K key,V vale); 往里面添加一个键值对

public void putAll(Map<? extends K,?extends V>map); 添加一个键值对的、小的map

public V remove(Object key); 通过一个键移除一个值 

public int size();键值对的数量

public Collectign<V>values(); 值的集合

Guess you like

Origin www.cnblogs.com/xiaozhongfeixiang/p/11548563.html