集合框架之HashMap详解

在这里插入图片描述

一. 简介

  • HashMap是底层采用数组+链表实现的线程不安全Key-Value结构。

二. 原理

  1. 插入元素
    通过put()方法传递键值对时,键先调用hashCode()方法,并返回一个hash值,通过hash值对数组长度取余(即index = HashCode(Key) % Array.length)找到数组对应的位置,再遍历链表判断链表中是否有此Key,如果链表中已有此Key(即发生了哈希冲突,解决方法有:开放寻址法、链表法、再哈希法、建立公共溢出区),HashMap采用了链表法解决哈希冲突,则用新Value值代替旧Value值,若没有此Key则插入键值对到链表头部(Java8之前)或链表尾部(java8之后)。

    开放寻址法:寻找下一个空的散列地址;
    链表法:头插法和尾插法;
    再哈希法:有多个不同的Hash函数,使用第二个,第三个,….,等哈希函数计算地址,直到无冲突;
    建立公共溢出区:将哈希表分为基本表和溢出表两部分,和基本表发生冲突的元素填到溢出表
    
  2. 初始化大小:16

  3. 扩容机制
    (1)影响扩容的因素
    Capacity:即HashMap的当前长度;
    LoadFactor:即HashMap的负载因子,默认是0.75f。

    (2)衡量扩容的条件:HashMap.Size >= Capacity x LoadFactor

    (3)扩容步骤
    扩容:创建一个新的Entry空数组,长度是原来数组的2倍
    ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组中,其目的就是将原来的Entry尽可能均匀的分配。

  4. 头插法和尾插法
    「头插法」:Java8之前,采用头插法,即将键值对插入到链表头部。扩容时可能会改变元素在新数组中的位置,在并发情况下,可能会出现环形链表

    「尾插法」:Java8之前后,采用尾插法,即将键值对插入到链表尾部。扩容时不会改变元素在新数组中的位置,不会出现环形链表。

  5. java8的改进
    (1)Java8后,采用数组+链表+红黑树实现,链表大小大于等于8时,会自动转为红黑树;当删除小于6时,重新变为链表;
    (2)Java8之前,采用头插法;Java8之前后,采用尾插法。

三. HashMap封装为线程安全

  • 可通过Collections.synchronizedMap()把一个HashMap封装为线程安全;
  • 原理:SynchronizedMap内部维护了一个Map和互斥锁mutex。

四. HashMap和Hashtable的比较

  • 同:二者的元素均是无序的,负载因子均是0.75;
HashMap Hashtable
初始化容量 16 11
扩容机制 2倍 2倍+1
父类 AbstractMap Dictionary
迭代器 Iterator Enumerator
快速/安全失败 fail-fast(快速失败) fail-safer(安全失败)
发布了70 篇原创文章 · 获赞 4 · 访问量 6376

猜你喜欢

转载自blog.csdn.net/qq_44837912/article/details/104378479