zoukankan      html  css  js  c++  java
  • HashMap

    1.什么是HashMap?

    HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫Entry.这些个键值对分散存储在一个数组中,这个数组就是HashMap的主干,数组的每个元素初始化为null,

    1,为什么用了一维数组:数组存储区间是连续的,占用内存严重,故空间复杂度很大。但数组的查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难

    2,为什么用了链表:链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易而HashMap是两者的结合,用一维数组存放散列地址,以便更快速的遍历;用链表存放地址值,以便更快的插入和删除!

    2.HashMap的Put Get方法

    Put
    调用put方法例如调用hashMap.put("apple",0),会在数组中插入一个key为"apple"的元素,通过一个hash函数确定Entry的插入位置index=hash("apple")
    但是数组的长度有限,可能会发生index冲突,HashMap数组的每一个元素是一个Entry,也是一个链表的头结点,当新来的Entry映射发生冲突时,会使用头插法(发明者认为后插入的Entry被查找的可能性大),将其插入链表中,每一个Entry对象通过Next指针指向它的下一个结点
    Get
    首先会将输入的key做一次映射,得到对应的index,查看头结点的key是否是target,如果是就找到了;如果不是就查看next结点

    3.HashMap长度

    HashMap默认初始长度16,并且每次自动扩展或者手动初始化时,长度必须是2的幂
    Hash: index=HashCode(Key)&(length-1);
    如果长度不是2的整数幂,会增加重复几率的
    而且2的整数幂-1其实二进制都是1,相当于取HashCode的后几位,尽量保证HashCode分布均匀

    4.HashMap的扩充

    HashMap的容量是有限的,经过多次的插入,会达到一定的饱和度,Key映射位置发生冲突的几率提高,所以要Resize扩充HashMap数组

    Resize的因素:
    Capacity:HashMap的当前长度
    LoadFactor:HashMap得负载因子,默认值为0.75f

    HashMap是否能进行Resize的条件:
    HashMap.size >= Capacity * LoadFactor

    Resize步骤:
    扩容:创建一个新的Entry空数组,长度是原来的两倍
    ReHash:遍历原来的数组,把所有的Entry重新Hash到新数组.

    ReHash源码:

    /**
     * Transfers all entries from current table to newTable.
     */
    void transfer(Entry[] newTable, boolean rehash) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) {
            while(null != e) {
                Entry<K,V> next = e.next;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
                newTable[i] = e;
                e = next;
            }
        }
    }

    5.HashMap出现环的原

    正常的Rehash过程

    假设表size=2,key=3,7,5 这里所使用的rehash算法就是index=key&(size-1)

    所以key=3,5,7的Entry对象都指向了数组index=1的位置

    而capacity=3,size=2,size<capacity*0.75

    所以数组应进行扩容.

    上面有数组扩容时,table rehash到newTable的代码,这里只摘取while循环的代码

    while(null != e) {
       Entry<K,V> next = e.next;
       if (rehash) {
          e.hash = null == e.key ? 0 : hash(e.key);
       }
       int i = indexFor(e.hash, newCapacity);
       e.next = newTable[i];
       newTable[i] = e;
       e = next;
    } 

    Rehash过程图:

    并发下的Rehash过程

    假设有两个线程:线程A和线程B

    1.线程A执行一行代码后挂起

    while(null != e) {
       Entry<K,V> next = e.next;  //线程A执行完这句时挂起
       if (rehash) {
          e.hash = null == e.key ? 0 : hash(e.key);
       }
       int i = indexFor(e.hash, newCapacity);
       e.next = newTable[i];
       newTable[i] = e;
       e = next;
    }   

    2.线程B全部执行完

    然而线程A: e指向key:3,next指向key:7,e.next=next;

           线程B: e指向key:3,next指向key:7,next.next=e

    3.这时线程A抢到了执行权,回来继续执行

    e.next=newTable[i]; //导致key3.next指向了null
    
    newTable[i]=e;      //线程A的index3指向了key3
    
    e=next;             //e指向了key7
    

      

    4.因为线程A执行的结果:key3.next=key7,所以下一次循环将key7插在key3所在链的头,然后e和next都往下移

     5.执行e.next=newTable[i];导致key3.next=key7,所以出现环形链表

  • 相关阅读:
    python 报错 AttributeError: 'Series' object has no attribute 'as_matrix'
    python 报错 NameError: name 'xrange' is not defined
    python报错 AxisError: axis 0 is out of bounds for array of dimension 0
    Python 列表中的浅拷贝与深拷贝
    python列表中查找元素
    Python中两个变量交换
    Python中*和**的使用
    Airtest启动报错:ERROR:gup_process_transport_factory.cc<1019>] Lost UI share context
    adb的使用
    Python函数
  • 原文地址:https://www.cnblogs.com/Hangtutu/p/8025854.html
Copyright © 2011-2022 走看看