Java集合精选常见面试题( 八 )


扩容(加载)因子为何默认是 0.75f

  1. 在空间占用与查询时间之间取得较好的权衡
  2. 大于这个值,空间节省了,但链表就会比较长影响性能
  3. 小于这个值,冲突减少了 , 但扩容就会更频繁,空间占用也更多
并发问题
  • 扩容死链(1.7 会存在): 原因是1.7头插法会改变元素顺序
  • 数据错误(1.7和1.8):并发环境下若多个待插入元素计算出来的索引下标相同则会产生覆盖问题
key 的设计key 的设计要求
  1. HashMap 的 key 可以为 null , 但 Map 的其他实现则不然
  2. 作为 key 的对象,必须实现 hashCode 和 equals,并且 key 的内容不能修改(不可变)
  3. key 的 hashCode 应该有良好的散列性
如果 key 可变,例如修改了 age 会导致再次查询时查询不到
几种Map接口实现类的区别1. HashMap 和 Hashtable 的区别
  1. 线程是否安全: HashMap 是非线程安全的,Hashtable 是线程安全的 , 因为 Hashtable 内部的方法基本都经过synchronized 修饰 。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧?。?/li>
  2. 效率: 因为线程安全的问题,HashMap 要比 Hashtable 效率高一点 。另外,Hashtable 基本被淘汰,不要在代码中使用它;
  3. 对 Null key 和 Null value 的支持: HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException
  4. 初始容量大小和每次扩充容量大小的不同 :① 创建时如果不指定容量初始值 , Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1 。HashMap 默认的初始化大小为 16 。之后每次扩充,容量变为原来的 2 倍 。② 创建时如果给定了容量初始值 , 那么 Hashtable 会直接使用你给定的大?。?而 HashMap 会将其扩充为 2 的幂次方大?。?code>HashMap 中的tableSizeFor()方法保证,下面给出了源代码) 。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小 , 后面会介绍到为什么是 2 的幂次方 。
  5. 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断 , 如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间 。Hashtable 没有这样的机制 。
HashMap 中带有初始容量的构造函数:
public HashMap(int initialCapacity,float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);}public HashMap(int initialCapacity) {this(initialCapacity,DEFAULT_LOAD_FACTOR);}下面这个方法保证了 HashMap 总是使用 2 的幂作为哈希表的大小 。
/*** Returns a power of two size for the given target capacity.*/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;}2. HashMap 和 HashSet 区别如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的 。(HashSet 的源码非常非常少,因为除了 clone()writeObject()readObject()HashSet 自己不得不实现之外 , 其他方法都是直接调用 HashMap 中的方法 。
HashMapHashSet实现了 Map 接口实现 Set 接口存储键值对仅存储对象调用 put()向 map 中添加元素调用 add()方法向 Set 中添加元素HashMap 使用键(Key)计算 hashcodeHashSet 使用成员对象来计算 hashcode 值,对于两个对象来说 hashcode 可能相同,所以equals()方法用来判断对象的相等性3. HashMap 和 TreeMap 区别TreeMapHashMap 都继承自AbstractMap

推荐阅读