java集合面试题2

avatar
作者
筋斗云
阅读量:0

Java 中 HashMap 的扩容机制是怎样的?

在 Java 中,HashMap 的扩容机制主要是为了保持其性能和数据存储的效率。

当 HashMap 中的元素数量达到一定的负载因子(默认值为 0.75)和当前容量的乘积时,就会触发扩容操作。

扩容时,会创建一个新的、容量更大的数组。新容量通常是原容量的两倍。

然后,需要将原数组中的所有元素重新计算其在新数组中的位置,并进行重新插入。

例如,如果原数组容量为 16 ,当元素数量达到 12 (16 * 0.75 = 12)时,就会进行扩容,新数组容量变为 32 。

这种扩容机制的优点在于,通过适当的扩容,可以避免因为数组过于拥挤导致的性能下降。但缺点是扩容操作本身需要消耗一定的时间和资源,包括创建新数组和重新计算元素位置并插入。

在实际使用中,如果能提前预估 HashMap 中元素的数量,通过指定合适的初始容量,可以减少扩容的次数,提高性能。

为什么 Java 中 HashMap 的默认负载因子是 0.75?

Java 中 HashMap 的默认负载因子被设定为 0.75 ,主要有以下几个原因:

空间和时间效率的平衡

  • 负载因子较小,如 0.5 ,会导致空间利用率低,因为需要更大的数组来存储相同数量的元素,浪费内存空间。
  • 负载因子较大,如 0.9 ,虽然能节省空间,但会增加哈希冲突的概率,导致链表或红黑树的长度增加,降低查找和插入的效率。

性能考虑

  • 0.75 这个值在大多数情况下能够提供较好的性能平衡,既能避免过多的空闲空间浪费,又能控制哈希冲突的频率在一个可接受的范围内。

经验和实践

  • 经过大量的测试和实践,发现 0.75 是一个在各种常见使用场景下表现较为良好的折中值。

例如,在一个包含大量数据的 HashMap 中,如果负载因子设定过高,可能会导致大量的哈希冲突,使得查找操作需要遍历较长的链表或红黑树,从而降低性能。相反,如果负载因子过低,虽然冲突减少,但会浪费过多的内存来存储可能并不多的元素。

什么是 Java 的 TreeMap?

TreeMap 是 Java 中的一种有序映射集合。

它基于红黑树数据结构实现,能够对键进行自然排序或者根据指定的比较器进行排序。

与 HashMap 不同,TreeMap 中的元素是按照键的顺序进行存储和访问的。

这意味着,当遍历 TreeMap 时,可以按照键的升序或指定的顺序获取元素。

例如,如果键是整数类型,那么它们会按照从小到大的顺序排列;如果键是自定义的对象类型,就需要提供相应的比较器来定义排序规则。

TreeMap 适用于需要按照特定顺序访问键值对的场景。比如,实现一个按照名称排序的学生信息存储,或者按照时间顺序存储事件信息等。

它的优点在于能够提供有序的访问,缺点是插入、删除和查找操作的时间复杂度相对较高,为 O(log n),而 HashMap 的平均时间复杂度为 O(1) 。但在需要有序性的情况下,TreeMap 是一个很好的选择。

ConcurrentModificationException 错误是如何产生的?

在 Java 中,ConcurrentModificationException 错误通常在并发修改集合时产生。

当一个线程正在遍历一个集合(如 ArrayList 、HashMap 等),而另一个线程同时对该集合进行修改(添加、删除元素等操作),就可能会抛出这个异常。

例如,有一个线程正在通过迭代器遍历一个 ArrayList :

List<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c");  Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) {     String element = iterator.next();     // 假设此时另一个线程删除了元素 "b" } 

而在这个过程中,另一个线程删除了其中的元素,那么当前遍历的线程就会抛出 ConcurrentModificationException 。

这是为了保证集合的一致性和防止不可预测的行为。

再比如,在多线程环境下,一个线程正在读取 HashMap 中的元素,而另一个线程对其进行修改,也可能导致这个异常的抛出。

为了避免这种情况,可以使用线程安全的集合类,如 ConcurrentHashMap ,或者在对集合进行操作时进行适当的同步控制。

文字丨代码星辰阁

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!