深入Map集合源码的几个实现

来源:互联网 发布:盈建科三维软件停止 编辑:程序博客网 时间:2024/06/13 20:01

一、对HashMap源码的深入理解

      1、HashMap 默认16个数组元素大小,加载因子0.75f
      2、记载因子用来计算下次扩容的临界值(寄不是Entry数组满了之后才进行扩容,而是到了数组到了扩容临界值就进行扩容)
      3、HashMap 能存入空key(也能存储null值),但是只能存一个null,多个null的key会被后面的覆盖,并且始终是存放在map第一位
      4、HashMap Entry数组里每个元素存储的是一个链表的头结点
      5、HashMap 存数据的过程
         1、根据hashcode计算hash值
         2、根据hash值和当前Entry大小计算Entry数组(hash%length)
         3、处理hash相同的情况下key释放一致:
            判断当前key的数组里面是否已经存在Entry对象,然后判断
            if(e.hash == hash && ((k = e.key) == key || key.equals(k)))
               hash和key相等,做数据的覆盖(HashMap的key不能重复)
         4、添加Entry数组
            判断当前数组大小是否已经到了扩容临界值并且当前数组位置已经存在数据(尽量减少hash冲突)
            if(size >= threshold) && (null != table[bucketIndex])
               resize(2 * table.length)扩容为当前的两倍
                  创建新数组并且把旧数组和新数组合并(影响效率操作)
               重新计算hash值和新的数组下标
            创建Entry元素
               Entry<K,V> e = table[bucketIndex] 
                  获取当前要插入位置的Entry元素(null或者是个链表)
               最终将最新插入数据放在了链表的头(before)
       6、HashMap 取数据
            通过hash值定位到Entry数组下标,然后通过遍历链表的方式判断hash和key是否都相等来定位到真实的数据



 二、对Hashtable源码的深入理解(源码比HashMap的粗糙)

       1、Hashtable 默认11个数组元素大小,加载因子0.75f
       2、Hashtable 最小数组是1(小于0直接抛出异常,等于0设置为1)
       3、Hashtable 不能存储null的值,否则直接抛出异常
       4、Hashtable 存数据属于线程安全(put方法上使用了sychronized关键字)
       5、Hashtable 存数据的过程
          1、value等于null直接抛出空异常(key等会null也会抛出异常,计算hashCode的时候)
          2、根据hashcode计算hash值
          3、根据hash值和一个十六进制的数与当前Entry大小计算Entry数组(hash & 0x7FFFFFFF) % tab.length
          4、处理hash相同的情况下key释放一致
             (e.hash == hash) && e.key.equals(key)
             如果一致直接覆盖
          5、Entry数组大小大于扩容临界值,进行hash重组
          6、创建Entry元素(与HashMap一致)



 三、对ConcurrentHashMap源码的深入理解(*在HashMap的基础上使用Segment做分片加锁操作,默认16个(concurrency level),然后每次操作对一个segment加锁,避免多线程锁的几率,提高并发效率)
       1、ConcurrentHashMap 默认16个数组元素大小,加载因子是0.75f,Segment大小默认为16(即16个Segment碎片)
       2、ConcurrentHashMap 是在HashEntry(类似于Entry)的基础上封装了一层Segment(碎片,每个碎片是个Hashtable线程安全)
       3、ConcurrentHashMap 使用的是锁分离技术(Segment)使用多个锁来控制对hash表的不同部分进行修改
       4、ConcurrentHashMap 存数据的过程
          1、value和key等于null会抛出异常
          2、根据hashCode计算hash值
          3、根据hash值计算所在的碎片(hash >>> segmentShift) & segmentMask
          4、从碎片中出HashEntry数组,放到一个新定义的Segment中
          5、往Segment中的HashEntry添加元素
             1、tryLock() ? null :  scanAndLockForPut(key, hash, value)  //判断是否已经把当前segment操作锁住
             2、计算当前key在HashEntry数组的位置(tab.length - 1) & hash
             3、判断是数组位置是否有元素(即获取链表的头节点)
                 做了一个死循环操作:目的是让操作只能在头部进行
                 头节点(first)不为空:如果hash值和key相等
                     if ((k = e.key) == key || (e.hash == hash && key.equals(k))) 做value的替换操作
               

                
 四、对HashMap和Hashtable的比较

       1、不设置大小的情况下默认大小不一样(HashMap数组长度是16、Hashtable默认数组长度是11)
       2、Hash支付空的key和value,Hashtable不支持(Hashtable存空key会直接报错)
       3、Entry数组下标位置的计算方式不一样
             HashMap:h & (length-1)
             Hashtable:(hash & 0x7FFFFFFF) % tab.length
       4、HashMap线程不安全,Hashtbale线程安全(sychronized效率比较低,锁住整张hash表)        
       5、Entry数组扩容规则不一样
             HashMap:2*table.length
             Hashtable:(oldCapacity << 1) + 1 (2N+1的规则) 
       6、方法的不同
             HashMap:有containsvalue和containsKey
             Hashtable:有contains方法方法
阅读全文
0 0
原创粉丝点击