Java岗面试题--Java基础(日积月累,每日三题)

面试题一:HashMap 与 HashTable 的区别是什么?

  1. 线程是否安全: HashMap 是非线程安全的, HashTable 是线程安全的,因为 HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧);
  2. 效率: 因为线程安全的问题, HashMap 要比 HashTable 效率高一点。另外, HashTable 基本被淘汰,不要在代码中使用;
  3. 对 Null key 和 Null value 的支持: HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有⼀个, null 作为值可以有多个; HashTable 不允许有 null 键和 null 值,否则会抛出 NullPointerException 。
  4. 初始容量大小和每次扩充容量大小的不同 :
    a. 创建时如果不指定容量初始值, Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。 HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。
    b. 创建时如果给定了容量初始值,那么Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。
  5. 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。 Hashtable 没有这样的机制。

面试题二:HashMap 的长度为什么是2的幂次方?

为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀。

Hash 值的范围值 -2147483648 到 2147483647,前后加起来⼤概40亿的映射空间,只要哈希函数映射得比较均匀松散,⼀般应用是很难出现碰撞的。但问题是⼀个40亿长度的数组,内存是放不下的。所以这个散列值是不能直接拿来用的。用之前还要先做对数组的长度取模运算,得到的余数才能用来要存放的位置也就是对应的数组下标。这个数组下标的计算方法是
“ (n - 1) & hash ”。(n代表数组长度)。这也就解释了 HashMap 的长度为什么是2的幂次方。

这个算法应该如何设计呢?
我们首先可能会想到采用 % 取余的操作来实现。但是,重点来了: “取余(%)操作中如果除数是2的幂次则等价于与其除数减⼀的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。 ” 并且采用⼆进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。

2的n次方的主要核心原因是hash函数的源码中右移了16位让低位保留高位信息,原本的低位信息不要,那么进行&操作另一个数低位必须全是1,否则没有意义,所以len必须是2^n,这样能实现分布均匀,有效减少hash碰撞!

为了提高HashMap的效率,减少数据的碰撞,使数据分布平均,用之前需要用hash值对数组进行求余运算,算出在数组中的位置,这个计算方法是(length-1)& hash,与hash对数组长度求余结果是一样的,这样效率更高,因为2的n次方是1后边n个0,2的n次方-1,就是n个1,所以为了减少数据碰撞,使分布均匀,需要使得hashmap的长度是2的n次方。

面试题三:CopyOnWriteArrayList 的底层原理是怎样的

  1. 首先 CopyOnWriteArrayList 内部也是用过数组来实现的,在向 CopyOnWriteArrayList 添加元素时,会复制一个新的数组,写操作在新数组上进行,读操作在原数组上进行;
  2. 并且,写操作会加锁,防止出现并发写入丢失数据的问题;
  3. 写操作结束之后会把原数组指向新数组;
  4. CopyOnWriteArrayList 允许在写操作时来读取数据,大大提高了读的性能,因此适合读多写少的应用场景,但是 CopyOnWriteArrayList 会比较占内存,同时可能读到的数据不是实时最新的数据,所以不适合实时性要求很高的场景。

但是 CopyOnWriteArrayList 有其缺陷:

  • 内存占用:在写操作时需要复制一个新的数组,使得内存占用为原来的两倍左右;
  • 数据不一致:读操作不能读取实时性的数据,因为部分写操作的数据还未同步到读数组中。
  • 所以 CopyOnWriteArrayList 不适合内存敏感以及对实时性要求很高的场景

猜你喜欢

转载自blog.csdn.net/weixin_44129618/article/details/128987520