java集合问题篇

● 请解释为什么集合类没有实现Cloneable和Serializable接口?

Java集合必会14问(精选面试题整理)

Java集合必会14问(精选面试题整理)

HashMap的put方法的具体流程?

首先判断数组是否为空(为空就初始化数组),不空----》key是否为空(空:将null插入到数组的0号位置),不空—》根据key算出hash值,再根据hash值算出将要插入的数组的下标【(数组长度-1)&hashcode】,如果该位置有元素了,则将该元素和要插入的元素进行比较(先比较hash,再比较key,相同,则覆盖其value,不同,则jdk1.7使用头插法将添加的元素加入到链表头,然后将链表下移。jdk1.8是使用尾插法。【1.8由于使用了红黑树,总是要遍历链表的,所以直接将其放入尾部】)插入元素后,计数变量:size++

hashmap如何扩容

初始化hashmap的时候会有默认的数组长度16,加载因子3/4,此时阈值为163/4=12;
当元素超过12个时就会进行扩容。新数组长度是16
2=32,阈值是32*3/4=24,然后将原数组的元素经过hash运算,重新计算下标后,再拷贝到新数组里面。

hashmap是如何解决hash冲突的

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);// 与自己右移16位进行异或运算(高低位异或)
}

先使用hash函数(即2次扰动函数【1次位运算和1次异或运算(2次扰动)】(计算出index))增加散列性,减少hash冲突。

如果发生了hash冲突,则采用拉链法(链地址法),解决冲突。jdk1.8还使用了红黑树来增加查询的效率。

bucket[index]=new Entry(K,V,null);   //没有hash冲突的时候put的逻辑
bucket[index]=new Entry(K,V,bucket[index]);  //hash冲突的时候put的逻辑--即链地址法bucket(大桶小桶装数据用的)是数组,index是要插入的数组的下标。
6)HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标?
答:hashCode()方法返回的是int整数类型,其范围为-(2 ^ 31)~(2 ^ 31 - 1),约有40亿个映射空间,而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap通常情况下是取不到最大值的,并且设备上也难以提供这么多的存储空间,从而导致通过hashCode()计算出的哈希值可能不在数组大小范围内,进而无法匹配存储位置;

面试官:那怎么解决呢?

答:

HashMap自己实现了自己的hash()方法,通过两次扰动使得它自己的哈希值高低位自行进行异或运算,降低哈希碰撞概率也使得数据分布更平均;

在保证数组长度为2的幂次方的时候,使用hash()运算之后的值与运算(&)(数组长度 - 1)来获取数组下标的方式进行存储,这样一来是比取余操作更加有效率,二来也是因为只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,三来解决了“哈希值与数组大小范围不匹配”的问题;

面试官:为什么数组长度要保证为2的幂次方呢?

答:

只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,即实现了key的定位,2的幂次方也可以减少冲突次数,提高HashMap的查询效率;

如果 length 为 2 的次幂 则 length-1 转化为二进制必定是 11111……的形式,在于 h 的二进制与操作效率会非常的快,而且空间不浪费;如果 length 不是 2 的次幂,比如 length 为 15,则 length - 114,对应的二进制为 1110,在于 h 与操作,最后一位都为 0 ,而 0001001101011001101101111101 这几个位置永远都不能存放元素了,空间浪费相当大,更糟的是这种情况中,数组可以使用的位置比数组长度小了很多,这意味着进一步增加了碰撞的几率,减慢了查询的效率!这样就会造成空间的浪费。

面试官:那为什么是两次扰动呢?

答:这样就是加大哈希值低位的随机性,使得分布更均匀,从而提高对应数组存储下标位置的随机性&均匀性,最终减少Hash冲突,两次就够了,已经达到了高位低位同时参与运算的目的;
发布了437 篇原创文章 · 获赞 82 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_41063141/article/details/103840102