面试-java集合之HashMap

Java之HashMap:

1.HashMap原理.
2.为什么要用链表?
3.可以用LinkedList代替数组吗?
4.那为什么不用ArrayList?
5.hashMap什么时候扩容?
6.预设的大小多少合适?
7.为什么扩容大小是2的n次方?
8.如果初始化时大小设置不是2的n次方会如何处理?

1.HashMap原理.

HashMap底层是一个储存Entry键值对对象的数组,每个Entry对象其实是一个单链表,利用Next指针指向下一个对象。

2.为什么要用链表?

用链表是为了解决Hash冲突问题,当出现Hash冲突时,把Hash值一样的对象放到同一个链表。放到链头,最先加入的在链尾。

3.可以用LinkedList代替数组吗?

可以,但是不如用数组效率高,因为HashMap是根据hash值%数组length来定位桶的位置,所以用数组来实现查找效率高。

4.那为什么不用ArrayList?

因为ArrayList的扩容机制是1.5倍,而hashMap的扩容机制是2的n次方,效率高,使用数组可以自定义扩容机制。

5.hashMap什么时候扩容?

当桶满了,超过(load factor *current capacity )就要resize, load factor为0.75,current capacity是数组大小,因为扩容时需要重新计算对象的位置,所以如果能提前计算需要的大小就会比较提交性能。

6.预设的大小多少合适?

一般来说要预设2的n次方个,原因下面说。如果预设需要1000个,这时如果定义1024个的话因为少于1024*0.75,所以需要定义2048个。

7.为什么扩容大小是2的n次方?
为了存取数据的高效,需要尽量减少哈希冲突,把数据均匀的分布,让每个链表的长度差不多,那么扩容大小为2的n次方是如何减少哈希冲突。数据添加的位置的算法是取模:hash值%数组长度;但是在源码中对它优化,采用的是更高效的位运算:hash & ( length-1) 。

2的n次方二进制表示为 1 后面加上 n 个 0 ,-1 后即为 n-1 个 1,这样 hash在做 &( length -1 )操作时,每一位都可以跟1做与运算,这样就可以保证 数组的均匀分布 ,假如:100个数,hash值从1~100,那么这100个数在数组中是平均分配的

并且注意:hash%length == hash & (length-1) 是在length 等于 2的n次方情况下才成立。

8.如果初始化时大小设置不是2的n次方会如何处理?

初始化数组大小时,会调用tableSizeFor()方法,把输入的值转换为大于并且最接近输入值大小的2的n次方的数。

tableSizeFor()源码:

static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

有关n的位移操作分析:

语法>>>i :向右移动i位,左边的数补0

先假设n为01xxx…xxx
对n右移1位:001xxx…xxx 再异或:011xxx…xxx
对n右移2位:00011xx.xxxx 再异或:01111xx…xxx
对n右移4位:000001111xx…xx 再异或:0111111111xx…xxx
结果是把最高位1后面的数变为1,最后再加1,等于2的整次数幂了。
让cap-1再赋值给n的目的是另找到的目标值大于或等于原值。例如二进制1000,十进制数值为8。如果不对它减1而直接操作,将得到答案10000,即16。显然不是结果。减1后二进制为111,再进行操作则会得到原来的数值1000,即8。

一个2的n次方的数 二进制是 1后面加 n 个 0 , 所以要把一个数变成 大于等于并且最接近这个数的2的n次方数,那么一个数所有位变成1 再加上 1 .
如 12 1100 先变成 1111 再加上 1 就是 16 10000 ,最开始减1 是 因为如果传进来的数 已经>是2的 n 次方了。

9.HashMap的put过程是怎么样的?

计算key的hash值( hash() 方法 )和length-1 做与运算 ,得出数组的index,

在这里插入图片描述

如果没冲突直接放到bucket中,
如果冲突了则以链表的形式添加到bucket后,
如果bucket链表的长度超过8,则转换位红黑树,
如果节点已经存在就替换old value(保证key的唯一性),
如果bucket满了(超过load factor*current capacity),就要resize。

10.key和value可以重复吗?
在这里插入图片描述
在这里插入图片描述
11.哈希冲突常用的解决方法

这里是引用

12.jdk8的哈希冲突解决方案

这里是引用
在这里插入图片描述

这里为什么使用单链表?

> 这里是引用
在添加元素时,如果key对应的bucket有元素,那么对往后遍历链表每一个元素,判断是否是相同的key如果是的话就覆盖他,如果不是就遍历到最后插入到最后一个位置,整个过程不用往回遍历链表,所以使用单链表就好。

13.如何生成key的Hash值

1.Int:整数值当哈希值
在这里插入图片描述
2.Float:将储存的二进制格式转换为十进制
在这里插入图片描述
3.Double:
在这里插入图片描述
4.Long:
在这里插入图片描述
在这里插入图片描述
相当于把前32位和后32位异或运算,放在后32位,再通过 (int)把数据类型转换为int,砍掉前32位的值。
充分利用所有信息计算出哈希值

String:
在这里插入图片描述
思路:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
为什么使用31作为权值:
在这里插入图片描述

14.HashCode和equals分别在什么时候调用?

HashCode在计算索引值时调用;
equals在哈希冲突时对比key值时调用;

给
区别:如果obj是

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值