一.什么是哈希碰撞

哈希碰撞是指在哈希表中出现两个或多个键(key)产生了相同的哈希值的情况,这就是哈希冲突或哈希碰撞。在哈希表中,通过哈希函数将键映射到索引的过程中,由于数据的不确定性,可能会使多个键映射到相同的索引位置,这样就会产生哈希碰撞。为了解决哈希碰撞问题,通常会使用一些技术,如散列链表、开放定址、再哈希等。哈希碰撞会导致哈希表的性能下降,因为需要额外的时间来解决冲突。

二.HashMap中装载因子,以及扩容规则

HashMap中的装载因子(load factor)是指当哈希表中存储的元素个数达到总容量的百分比时,就会触发扩容操作。装载因子的大小可以通过构造函数指定,默认值为0.75。

扩容是当HashMap中元素的数目达到阈值时,自动增加容量,调整哈希表的大小,以便容纳更多的元素。根据Java官方文档指出,当HashMap中的元素数量超过负载因子×当前哈希表长度时,就需要对HashMap进行扩容。具体的扩容规则如下:

  1. 创建一个新的哈希表,大小是原先的两倍。
  2. 遍历原哈希表中所有的元素,重新计算它们在新哈希表中的位置,并将它们复制到新的哈希表中。
  3. 将新哈希表赋值给HashMap底层的数组,完成扩容。

注意,扩容涉及到重新哈希所有的原先存储的元素,耗时比较长,因此HashMap的初始容量应该根据需要预估,以减少扩容的次数。

三. HashMap的高频面试题和难点

  1. HashMap底层实现原理是什么?
    HashMap底层实现原理是基于哈希表实现的。具体来说,它是由一个数组和一个链表构成的,每个数组元素被称为桶(bucket),每个桶里面有一个链表,用于解决哈希碰撞的问题。当使用put()方法将键值对存入HashMap时,首先根据键的哈希值计算出对应的桶,然后将键值对添加到相应的链表中。

  2. 什么情况下会引起HashMap的哈希冲突?
    当两个不同的键具有相同的哈希值时,会发生哈希冲突。

  3. HashMap的扩容机制是什么?
    当HashMap存储的元素数量超过负载因子×当前哈希表长度时,就需要对HashMap进行扩容。具体的扩容规则如下:

    1. 创建一个新的哈希表,大小是原先的两倍。
    2. 遍历原哈希表中所有的元素,重新计算它们在新哈希表中的位置,并将它们复制到新的哈希表中。
    3. 将新哈希表赋值给HashMap底层的数组,完成扩容。
  4. 如何避免HashMap的哈希碰撞问题?
    可以通过以下几种方式避免哈希碰撞:

    • 实现好的哈希函数:实现一个好的哈希函数可以将哈希值的分布均匀化,减少哈希碰撞发生的概率。
    • 扩容:扩容可以增加哈希表的空间,减少碰撞的概率。
    • 开放地址法:开放地址法可以是空间连续,降低哈希冲突的概率。
    • 链式存储法:使用链表来解决哈希碰撞问题。
  5. HashMap和ConcurrentHashMap有什么区别?
    HashMap是非线程安全的,而ConcurrentHashMap是线程安全的,可以进行高并发读写操作。ConcurrentHashMap的主要实现方式是对哈希桶进行分段锁定,即将整个HashMap分成多个段,每个段独立进行加锁,这样可以在多线程并发访问中提高性能。

  6. 如何使用迭代器遍历HashMap?
    使用迭代器可以遍历HashMap的元素,可以对键值进行迭代,例如:

    
    
    Map<String, String> map = new HashMap<>();
    map.put("key1", "value1");
    map.put("key2", "value2");
    Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
    while(it.hasNext()){
       Map.Entry<String, String> entry = it.next();
       System.out.println("key=" + entry.getKey() + ", value=" + entry.getValue());
    }
  7. 如何避免HashMap死循环问题?
    在多线程环境下,当HashMap被扩容时,如果有多个线程同时访问HashMap的话,就有可能导致HashMap出现死循环的情况。为了避免此问题,可以使用ConcurrentHashMap来代替HashMap,或者在使用HashMap时,将其限制于单线程环境下,或者采用Collections.synchronizedMap或者手动实现线程同步控制策略等方式来保证线程安全。

四.HashMap HashTable HashTree HashSet TreeSet ConcurrentHashMap 比较

  1. HashMap:
    • 优点:顺序、速度非常快,具备极好的查找性能。
    • 缺点:非线程安全,扩容代价较高
  2. HashTable:
    • 优点:与HashMap类似,线程安全,支持并发读写。
    • 缺点:同步代价较高,多线程效率低下
  3. HashTree:
    • 优点:对元素有序,能够很快地查找到元素位置。
    • 缺点:插入删除效率较低,占用较多内存空间。
  4. HashSet:
    • 优点:存储元素无序,查询速度快。
    • 缺点:非线程安全,无序。
  5. TreeSet:
    • 优点:对元素有序,提供有关排序的方法。
    • 缺点:插入删除效率较低,比HashSet占用更多的内存空间。
  6. ConcurrentHashMap:
    • 优点:线程安全,高并发读写,性能好。
    • 缺点:内部实现较为复杂,可能存在一定的问题风险。

需要根据具体需求和场景选择适合的集合。如果不需要考虑线程安全性和元素有序性,可以选择使用HashMap或HashSet,如果需要考虑线程安全性和高并发读写,可以选择ConcurrentHashMap,如果需要对元素有序并且需要进行排序操作,可以选择TreeSet,如果需要对元素有序但不需要排序操作,可以选择TreeMap或LinkedHashMap。

42 Comments

  1. Najlepsza aplikacja do kontroli rodzicielskiej, aby chronić swoje dzieci – potajemnie tajny monitor GPS, SMS-y, połączenia, WhatsApp, Facebook, lokalizacja. Możesz zdalnie monitorować aktywność telefonu komórkowego po pobraniu i zainstalowaniu apk na telefonie docelowym.

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注