集合Map详述---HashMap、HashTable(底层实现原理)

一、Map概述

1、Map特点 

(1)Map和Collection没有继承关系,是单独的一个分支

(2)Map集合以键值对方式存储 <key,value>

           key和value都是引用数据类型,存储的是对象的内存地址

           key起到主导地位,value是key的一个附属品

2、Map接口中常用方法

向Map集合中添加键值对---------V     put(K,V)

通过key获取value--------------V      get(key)

获取map集合中所有key-------Set     keySet()   所有的键是一个set集合

获取map集合中键值对的个数----int    size()

获取map集合中所有value-----Collection values()

通过key删除键值对------------V       remove(key)

清空map集合-----------------void    clear()

判断是否包含某个key---------boolean  cotainsKey(key)

判断是否包含某个value---------boolean  cotainsValue(value)

判断是否是否为空,元素个数为0---------boolean  isEmpty()

Map集合转成Set集合--------- Set<Map.Entry<K,V>>   entrySet()    
    将map中的key和value转成一个set集合,set集合中存储的对象为Map.Entry类型对象,对象包含key和value两个属性

3、Map集合的遍历

Map<Integer,String> map=new HashMap<>();
map.put(1,"zs");
map.put(2,"ls");
map.put(3,"ww");

(1)获取所有key,通过遍历key,来遍历value

  •      迭代器
//获得所有Key---Set集合
Set<Integer> keys=map.KeySet();

//迭代器遍历
Iterator<Integer> it=keys.iterator();
while(it.hasNext()){
   System.out.println(it.next());
} 

foreach

//获得所有Key---Set集合
Set<Integer> keys=map.KeySet();

//foreach迭代
for(Integer k:keys){
   System.out.println(k+"--->"+map.get(k)); //1--->zs
}

(2)将map集合全部转成set集合,遍历set集合(适合大数据量)

//Map集合--->Set集合(key部分是一个Entry数组)
Set<Map.Entry<Integer,String>,String> set=map.entrySet();

//迭代器遍历
Iterator<Map.Entry<Integer,String>> it=set.iterator();
while(it.hasNext()){
   System.out.println(it.next());
} 

//foreach遍历
for(Map.Entry<Integer,String> node:set){
   System.out.println(node.getKey()+"--->"+node.getValue()); //1=zs
}

二、HashMap

(一)底层结构

   1、哈希表=数组+单向链表

   2、哈希表是一个数值和单向链表的结合体,融合了两种数据结构的优点

        数组:查询快

        链表:增删改快

3、HashMap的key value实质是存在Entry中,Entry是hashMap中封装key-value键值对的

 此部分来源:(1条消息) hashmap的实现原理 数组 entry_Mar.三月-CSDN博客_entry数组 

 

(二)底层源码

  HashMap底层实际是一个一维数组,数组的元素是一个单向链表

(三)底层实现原理

1、概述实现过程   

   (1)采用hash算法将要存储的对象存储到数组的对应位置,

    (2)当元素较多时,容易出现hash冲突,即多个元素需存储到同一个数组下标位置

    (3)解决:单向链表,多个元素以链表形式存储在同一个数组下标下

   (4)当单向链表节点数大于8时,链表转成红黑树------几率极小,因为会数组扩容

2、hash算法

 (1)数组的初始容量值为什么是16?

  • 数组初始值是16,则数组下标是0-15
  • 15的二进制数-----1111
  • 任何多位二进制数和1111进行与操作的值范围均在0-15(多位二进制位数>=4)

(2)hash算法如何将数据放入map中

  •  每个键值对的key,都有一个hashCode------多位的二进制数(>4)
  • 将key的hashCode和数组容量-1的二进制数进行与操作------得到key的hash值,即数组的下标

 key hashCode      1010111010011101

 数组容量-1  15                             1111

 与操作                                          1101   (相当于取hashCode后四位)

1101------>13,则该键值对存放在数组的下标13位置

3、hash碰撞

 (1)当key通过hash算法得到的哈希值相同时------hash碰撞(hash冲突)

 (2)解决:链表

  •  将同哈希值的key及value以链表形式存储在同下标下
  • 新来的相同哈希值的键值对用尾插法存到同下标下的链表中

        1.7及之前----头插法------死锁问题(多线程)

         1.8及之后----尾插法

  • 当链表的节点数>8------链表转换成红黑树(平衡二叉树)
  • 由于数组会不断扩容,链表转换成红黑树的概率为千万分之一

4、数组扩容

  •    数组扩容因子是0.75, 当数组存储量超过0.75时,数组就会2倍扩容

            初始值:16     超过16*0.75=12  >12扩容2倍--->32

  • 为什么2倍扩容------因为这样才能得到全1二进制数进行与操作
  • 数组扩容:将小数组复制到大容量数组,会2次哈希

5、数组元素迁移---rehash 

(1)JDK7

  在准备好新的数组后,map会遍历数组的每个链表,然后遍历链表中的每个Entity,

  重新计算其hash值(也有可能不计算),找到新数组中的对应位置,

  以头插法插入新的链表。

【注】

  • 是否要重新计算hash值的条件这里不深入讨论,读者可自行查阅源码。
  • 因为是头插法,因此新旧链表的元素位置会发生转置现象。
  • 元素迁移的过程中在多线程情境下有可能会触发死循环(无限进行链表反转)。

(2)JDK8

由于数组的容量是以2的幂次方扩容的,

那么一个Entity在扩容时,新的位置要么在原位置,要么在原长度+原位置的位置。

原因如下图:

数组长度变为原来的2倍,表现在二进制上就是多了一个高位参与数组下标确定

一个元素通过hash转换坐标的方法计算后,恰好出现一个现象:

最高位是0则坐标不变

高位是1则坐标变为“10000+原坐标”,即“原长度+原坐标”。

  元素迁移部分来源:HashMap的扩容机制 - 知乎

三、HashTable----properties

1、HashTable的底层是哈希表

2、hashtable的key和value都不可以为null(hashmap可以)

3、HashTable是线程安全的,所有方法都带有synchronized关键字,效率较低

4、HashTable的初始化容量是11

     集合扩容是:原容量*2+1

5、properties----继承HashTable,线程安全

  (1)properties的存取

Properties p=new Properties ();
//存 setProperty(key,value)
p.setProperty("username","root");
p.setProperty("password","123");

//取 getProperty(key)
String uname=p.getProperty("username");
String pwd=p.getProperty("password");

   (2)常见使用:配置文件 xx.properties

                          反射机制 灵活存取不同对象class信息

四、SoretMap----TreeMap

            TreeMap底层是二叉树,可排序Map-----TreeSet(详见TreeSet)

附:老杜总结图

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值