Map集合的常见面试题

Posted yjc1605961523

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Map集合的常见面试题相关的知识,希望对你有一定的参考价值。

Q:HashMap 的工作原理?

A:HashMap 底层是 hash 数组单向链表实现,数组中的每个元素都是链表,由 Node 内部类(实现 Map.Entry<K,V>接口)实现,HashMap 通过 put & get 方法存储和获取。

存储对象时,将 K/V 键值传给 put() 方法:①、调用 hash(K) 方法计算 K 的 hash 值,然后结合数组长度,计算得数组下标;②、调整数组大小(当容器中的元素个数大于 capacity * loadfactor 时,容器会进行扩容resize 为 2n);
③、i.如果 K 的 hash 值在 HashMap 中不存在,则执行插入,若存在,则发生碰撞
ii.如果 K 的 hash 值在 HashMap 中存在,且它们两者 equals 返回 true,则更新键值对
iii. 如果 K 的 hash 值在 HashMap 中存在,且它们两者 equals 返回 false,则插入链表的尾部(尾插法)或者红黑树中(树的添加方式)

JDK 1.7 之前使用头插法JDK 1.8 使用尾插法
(注意:当碰撞导致链表大于 TREEIFY_THRESHOLD = 8 时,就把链表转换成红黑树)

获取对象时,将 K 传给 get() 方法:①、调用 hash(K) 方法(计算 K 的 hash 值)从而获取该键值所在链表的数组下标;②、顺序遍历链表,equals()方法查找相同 Node 链表中 K 值对应的 V 值。

hashCode 是定位的,存储位置;equals是定性的,比较两者是否相等


Q:当两个对象的 hashCode 相同会发生什么?

A:因为 hashCode 相同,不一定就是相等的(equals方法比较),所以两个对象所在数组的下标相同,"碰撞"就此发生。又因为 HashMap 使用链表存储对象,这个 Node 会存储到链表中。


Q:你知道 hash 的实现吗?为什么要这样实现?

A:JDK 1.8 中,是通过 hashCode() 的高 16 位异或低 16 位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度,功效和质量来考虑的,减少系统的开销,也不会造成因为高位没有参与下标的计算,从而引起的碰撞

Q:为什么要用异或运算符?
A:保证了对象的 hashCode 的 32 位值只要有一位发生改变,整个 hash() 返回值就会改变。尽可能的减少碰撞。


Q:HashMap 的 table 的容量如何确定?loadFactor 是什么? 该容量如何变化?这种变化会带来什么问题?

A:①、table 数组大小是由 capacity 这个参数确定的,默认是16,也可以构造时传入,最大限制是1<<30;
②、loadFactor 是装载因子,主要目的是用来确认table 数组是否需要动态扩展,默认值是0.75,比如table 数组大小为 16,装载因子为 0.75 时,threshold 就是12,当 table 的实际大小超过 12 时,table就需要动态扩容;
③、扩容时,调用 resize() 方法,将 table 长度变为原来的两倍(注意是 table 长度,而不是 threshold)
④、如果数据很大的情况下,扩展时将会带来性能的损失,在性能要求很高的地方,这种损失很可能很致命。


Q:HashMap 的遍历方式及其性能对比

A:主要四种方式:
NO.1:for-each map.keySet() -- 只需要K值的时候,推荐使用

for (String key : map.keySet()) {
    map.get(key);
}

NO.2:for-each map.entrySet() -- 当需要V值的时候,推荐使用

for (Map.Entry<String, String> entry : map.entrySet()) {
    entry.getKey();
    entry.getValue();
}

NO.3:for-each map.entrySet() + 临时变量

Set<Map.Entry<String, String>> entrySet = map.entrySet();
    for (Map.Entry<String, String> entry : entrySet) {
        entry.getKey();
        entry.getValue();
}

NO.4:for-each map.entrySet().iterator()

Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
        Map.Entry<String, String> entry = iterator.next();
        entry.getKey();
        entry.getValue();
}

Q:HashMap,LinkedHashMap,TreeMap 有什么区别?

A:HashMap 参考其他问题;
LinkedHashMap 保存了记录的插入顺序,在用 Iterator 遍历时,先取到的记录肯定是先插入的;遍历比 HashMap 慢;
TreeMap 实现 SortMap 接口,能够把它保存的记录根据键排序(默认按键值升序排序,也可以指定排序的比较器)


Q:HashMap & TreeMap & LinkedHashMap 使用场景?

A:一般情况下,使用最多的是 HashMap
HashMap:在 Map 中插入、删除和定位元素时;
TreeMap:在需要按自然顺序或自定义顺序遍历键的情况下;
LinkedHashMap:在需要输出的顺序和输入的顺序相同的情况下。


Q:HashMap 和 HashTable 有什么区别?

A:①、HashMap 是线程不安全的,HashTable线程安全的;
②、由于线程安全,所以 HashTable 的效率比不上 HashMap;
③、HashMap最多只允许一条记录的键为null,允许多条记录的值为null,而 HashTable 不允许;
④、HashMap 默认初始化数组的大小为16HashTable11,前者扩容时,扩大两倍,后者扩大两倍+1
⑤、HashMap 需要重新计算 hash 值,而 HashTable 直接使用对象的 hashCode


Q:Java 中的另一个线程安全的与 HashMap 极其类似的类是什么?同样是线程安全,它与 HashTable 在线程同步上有什么不同?

A:ConcurrentHashMap 类(是 Java并发包 java.util.concurrent 中提供的一个线程安全且高效的 HashMap 实现)。
HashTable 是使用 synchronize 关键字加锁的原理(就是对对象加锁);
而针对 ConcurrentHashMap,在 JDK 1.7 中采用 分段锁的方式;JDK 1.8 中直接采用了CAS(无锁算法)+ synchronized


Q:HashMap & ConcurrentHashMap 的区别?

A:除了加锁,原理上无太大区别。

另外,HashMap 的键值对允许有null,但是ConCurrentHashMap 都不允许


Q:为什么 ConcurrentHashMap 比 HashTable 效率要高?

A:HashTable 使用一把锁(锁住整个链表结构)处理并发问题,多个线程竞争一把锁,容易阻塞

ConcurrentHashMap
JDK 1.7 中使用分段锁(ReentrantLock + Segment + HashEntry),相当于把一个 HashMap 分成多个段,每段分配一把锁,这样支持多线程访问。锁粒度:基于 Segment,包含多个 HashEntry。
JDK 1.8 中使用 CAS + synchronized + Node + 红黑树。锁粒度:Node(首结点)(实现 Map.Entry<K,V>)。锁粒度降低了。

有可能问道你都知道哪些常用的Map集合?

  HashMap、HashTable、LinkedHashMap、ConcurrentHashMap。

Collection集合接口和Map接口有什么关系?

  没关系,Collection是List、Set父接口不是Map父接口。

HashMap是线程安全的吗?线程安全的Map都有哪些?性能最好的是哪个?

  HashMap不是线程安全的。线程安全的有HashTable、ConcurrentHashMap、SynchronizedMap,性能最好的是ConcurrentHashMap。

使用HashMap有什么性能问题吗?

  使用HashMap要注意避免集合的扩容,它会很耗性能,根据元素的数量给它一个初始大小的值。

HashMap的数据结构是怎样的?默认大小是多少?内部是怎么扩容的?

  HashMap是数组和链表组成的,默认大小为16,当hashmap中的元素个数超过数组大小*loadFactor(默认值为0.75)时就会把数组的大小扩展为原来的两倍大小,然后重新计算每个元素在数组中的位置。

怎么按添加顺序存储元素?怎么按A-Z自然顺序存储元素?怎么自定义排序? 

 按添加顺序使用LinkedHashMap,按自然顺序使用TreeMap,自定义排序TreeMap(Comparetor c)。

HashMap的链表结构设计是用来解决什么问题的?

  HashMap的链表结构设计是用来解决key的hash冲突问题的。

HashMap的键、值可以为NULL吗?HashTable呢?

  HashMap的键值都可以为NULL,HashTable不行。

HashMap使用对象作为key,如果hashcode相同会怎么处理?

  key的hash冲突,如果key equals一致将会覆盖值,不一致就会将值存储在key对应的链表中。

HashMap中的get操作是什么原理?

  先根据key的hashcode值找到对应的链表,再循环链表,根据key的hash是否相同且key的==或者equals比较操作找到对应的值。

HashMap 和 Hashtable 有什么区别?

  存储:HashMap 允许key 和 value 为 null,? Hashtable 不允许。
  线程安全:Hashtable 是线程安全的,? HashMap 是?线程安全的。
  推荐使?:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使?,推荐 在 单 线 程 环 境 下 使 ? HashMap 替 代 , 如 果 需 要 多 线 程 使 ? 则 ?ConcurrentHashMap 替代。

如何决定使? HashMap 还是 TreeMap?

  对于在 Map 中插?、删除、定位?个元素这类操作,HashMap 是最好的选择,因为相对?? HashMap 的插?会更快,但如果你要对?个 key 集合进?有序的遍历,那TreeMap 是更好的选择。 

说?下 HashMap 的实现原理?

  HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传? key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将value 保存在 bucket ?。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是?链表和红?树存储相同 hash 值的 value。当 hash 冲突的个数?较少时,使?链表否则使?红?树。
  ??理解:两个不同名称的key但是计算出的hashCode值相同的数据存储在链表或者红?树中。

哪些集合类是线程安全的?(可能问道)

  Vector、Hashtable、Stack 都是线程安全的,?像 HashMap 则是?线程安全的,不过在 JDK 1.5 之后随着 Java. util. concurrent 并发包的出现,它们也有了??对应的线程安全类,?如 HashMap 对应的线程安全类就是 ConcurrentHashMap。 

以上是关于Map集合的常见面试题的主要内容,如果未能解决你的问题,请参考以下文章

Mysql五连问的常见面试题

redis集群的常见面试题

Mybatis 的常见面试题

vuex的常见面试题

TCP协议的常见面试题

TCP和UDP的常见面试题