十、JavaSE-Map接口

  1. HashMap和Hashtable的区别

HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,在只有一个线程访问的情况下,效率要高于Hashtable。

  1. 如何决定使用HashMap还是TreeMap

对于Map中插入、删除和获取元素这类操作,HashMap是最好的选择

然而,假如需要对一个有序的key集合进行遍历,TreeMap是更好的选择。

  1. HashMap和ConcurrentHashMap的区别

  • HashMap不是线程安全的,而ConcurrentHashMap是线程安全的

  • ConcurrentHashMap采用锁分段技术,将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,然后再这个片分段上面进行插入,而且这里还需要获取segment锁。

  • ConcurrentHashMap让锁的粒度更精细一些,并发性能更好。

  1. 谈一下HashMap中put是如何实现的

  • 计算关于key的hashcode值(于Key.hashcode的高16位做异或运算)

  • 如果散列表为空时,调用resize()初始化散列表

  • 如果没有发生碰撞,直接添加元素到散列表中去

  • 如果发生了碰撞(hashcode值相同),进行三种判断

若key地址相同或者equals后内容相同,则替换旧值

如果是红黑树结构,就调用树的插入方法

链表结构,循环遍历直到链表中某个节点为空,尾插法进行插入,插入后判断链表个数是否达到变成红黑树的阙值8;也可以遍历到有节点与插入元素的哈希值和内容相同,进行覆盖。

  • 如果桶满了大于阀值,则resize进行扩容

  1. 谈一下HashMap中什么时候需要进行扩容,扩容resize()又是如何实现的?

调用场景:

  • 初始化数组table

  • 当数组table的size达到阙值时即++size>load factor*capacity时,也是在putVal函数中

实现过程:

  • 通过判断旧数组的容量是否大于0来判断数组是否初始化过

否:进行初始化

判断是否调用无参构造器

是:使用默认的大小和阙值

否:使用构造函数中初始化的容量,当然这个容量是经过tableSizefor计算后的2次幂数

概括的讲:扩容需要重新分配一个新数组,新数组是老数组的2倍长,然后遍历整个老结构,把所有的元素挨个重新分配到新结构中去。

PS:可见底层数据结构用到了数组 ,到最后会因为容量问题都需要进行扩容操作

  1. HashMap中get是如何实现的?

对key的hashcode进行hashing,与运算计算下标获取bucket位置,如果在桶的首位上就可以找到就直接返回,否则在树中找或者链表中遍历找,如果hash冲突,则利用equals方法遍历链表查找节点。

  1. 为什么不直接将key作为哈希值而是与高16位做异或运算?

因为数组位置的确定用的是与运算,仅仅最后四位有效,设计者将key的哈希值与高16位做异或运算使得在做&运算确定数组的插入位置时,此时的低位实际时高位与低位的结合,增加了随机性,减少了哈希碰撞的次数。

  1. 为什么是16?为什么必须是2的幂?如果输入值不是2的幂比如10会怎么样?

HashMap默认初始化长度为16,并且每次自动扩展或者是手动初始化容量时,必须是2的幂。

  • 为了数据的均匀分布,减少哈希碰撞。因为确定数组位置是用的位运算,若数据不是2的次幂则会增加哈希碰撞的次数和浪费数组空间。(PS:其实若不考虑效率,求余也可以就不用位运算了也不用长度必须为2的次幂)

  • 输入数据若不是2的次幂,HashMap通过一通位移运算和或运算得到的肯定是2的幂次数,并且是离那个数最近的数字

  1. 当两个对象的hashcode相等时会怎么样?

会产生哈希碰撞,若key值相同则替换旧值,不然链接到链表后面,链表长度超过阙值8就转为红黑树存储

  1. 如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?

超过阙值就会进行扩容操作,概括的讲就是扩容后的数组大小是原数组的2倍,将原来的元素重新hashing放入新的散列表中去。

PS:hashing(散列法或哈希法)的概念 散列法(Hashing)是一种将字符组成的字符串转换为固定长度(一般是更短长度)的数值或索引值的方法,称为散列法,也叫哈希法。由于通过更短的哈希

  1. 传统HashMap的缺点(为什么引入红黑树?):

JDK1.8以前HashMap的实现是数组+链表,即使哈希函数取得再好,也很难达到元素百分百均匀分布。当HashMap中有大量的元素存放在同一个桶中时,这个桶下有一条长长的链表,这个时候HashMap就相当于一个单链表,加入单链表有n个元素,遍历的时间复杂度就是O(n),完全失去了它的优势。针对这种情况,JDK1.8中引入了红黑树(查找时间复杂度为O(logn))来优化这个问题。

  1. 平时在、使用HashMap时一般使用什么类型的元素作为Key?

选择Integer,String这种不可变的类型,像String的一切操作都是新建一个String对象,对新的对象进行拼接分割等,这些类已经很规范的覆写了hashCode()以及equals()方法。作为不可变类天生是线程安全的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值