hashmap和hashtable的区别

hashmap和hashtable的区别

面试回答:

底层数据结构不同:jdk1.7底层都是数组+链表,但jdk1.8 HashMap加入了红黑树
Hashtable 是不允许键或值为 null 的,HashMap 的键值则都可以为 null。
添加key-value的hash值算法不同:HashMap添加元素时,是使用自定义的哈希算法,而HashTable是直接采用key的hashCode()
实现方式不同:Hashtable 继承的是 Dictionary类,而 HashMap 继承的是 AbstractMap 类。
初始化容量不同:HashMap 的初始容量为:16,Hashtable 初始容量为:11,两者的负载因子默认都是:0.75。
扩容机制不同:当已用容量>总容量 * 负载因子时,HashMap 扩容规则为当前容量翻倍,Hashtable 扩容规则为当前容量翻倍 +1。
支持的遍历种类不同:HashMap只支持Iterator遍历,而HashTable支持Iterator和Enumeration两种方式遍历
迭代器不同:HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。而Hashtable 则不会。
部分API不同:HashMap不支持contains(Object value)方法,没有重写toString()方法,而HashTable支持contains(Object value)方法,而且重写了toString()方法
同步性不同: Hashtable是同步(synchronized)的,适用于多线程环境,
而hashmap不是同步的,适用于单线程环境。多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。

原理:

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
}


2、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的扩容机制 - 知乎

3、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");
 

  • 24
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值