JavaSE之Map集合及其相关面试题

本文详细介绍了Java中的Map集合,包括Map的基本概念、特点、常用方法如put、remove等,重点讲解了HashMap、TreeMap和ConcurrentHashMap的区别,以及它们在处理键值对、线程安全性和哈希冲突等方面的原理。
摘要由CSDN通过智能技术生成

1.Map集合概念及特点

1.1 概念

interface Map<K,V> //K:键的类型 V:值的类型

1.2 Map集合的特点

  1.  键值对映射关系,一个键对应一个值
  2.  键不能重复,值可以重复元素存取无序

2. Map集合基本功能

方法名说明

V put(K key,V value)

添加元素
V remove(Object key)根据键删除键值对元素
void clear()移除所有的键值对元素
boolean containsKey(Object key)判断集合是否包含指定的键
boolean containsValue(Object value)判断集合是否包含指定的值
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中键值对的个数

3. Map集合的获取功能

方法名说明
V get(Object key)根据键获取值
Set<K> keySet()获取所有键的集合
Collection<V> values()获取所有值的集合
Set<Map.Entry<K,V>> entrySet()获取所有键值对对象的集合

4. Map集合的遍历

方法一:把Map集合的键集中起来,遍历键的集合,获取到每一个键,再根据键找对应的值。(代码如下)

public class MapDemo(){
public static void main(String[] args){
//创建集合对象
Map<String,String> map=new HashMap<String,String>();
//添加元素
map.put("恭喜","发财");
map.put("事事","如意");
map.put("平安","喜乐");
//获取所有键的集合。用KeySet()方法实现
Set<String> keySet =map.keySet();
//遍历键的集合,获取到每一个键。用增强for循环实现
for(String key:keySet){
//根据键的集合。用get(Object key)方法实现
String value=map.get(key);
System.out.println(key+","+value);
}
}
}

方法二:获取所有键值对对象的集合 (Set<Map.Entry<K,V>> entrySet())得到每一个键值对对象,用增强for循环得到每一个Map.Entry(),根据键值对对象来获取键和值,用getKey()得到键,用getValue()得到值(代码如下)

public class MapDemo01{
public static void main(String[] args){
//创建集合对象
Map<String,String> map=new HashMap<String,String>();
//添加元素
map.put("恭喜","发财");
map.put("事事","如意");
map.put("平安","喜乐");
//获取所有键值对的集合
Set<Map.Entry<String,String>> entrySet =map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for(Map.Entry<String,String> me:entrySet){
//根据键值对对象获取键和值
String key=me.getKey();
String value=me.getValue();
System.out.println(key+","+value);
}
}
}

5. HashMap集合

HashMap集合概念和特点

  1. HashMap底层是哈希表结构
  2. 依赖hashCode方法和equals方法保证键的唯一
  3. 如果要存储的是自定义对象,需要重写hashCode和equals方法

HashMap相关面试题

1.HashMap的特性?

  1. HashMap存储键值对实现快速存取,允许为null。key值不可以重复,若key值重复则覆盖。
  2. 非同步,线程不安全。
  3. 底层是hash表,不保证有序。

2.谈一下HashMap的JDK7和JDK8底层结构是什么?

  1. JDK7使用的是数组+链表。
  2. JDK8使用的是数组+链表+红黑树。

3.链表的作用是什么?为什么JDK8引入了红黑树?

  1. 链表主要是为了解决数组中key发生hash冲突时,将发生碰撞的key存到链表中
  2. 红黑树主要为了解决链表过长导致查询速度太慢问题,链表查询时间复杂度为O(n)
  3. 当链表长度大于等于8时,就会转变为红黑树,时间复杂度为O(logn)
  4. 当链表长度小于等于6时,由红黑树转变为链表,因为链表过短时引入红黑树反而会降低查询效率

4.什么是hash冲突?

当两个不同的值输入,根据同一散列函数计算相同的散列值的现象,我们称之为碰撞(Hash碰撞)。

5.有什么办法能减少hash冲突?

  1. 使用链地址法(使用散列表)来链接拥有相同hash值的数据,就是我们上文所说的链表。
  2. 使用两次扰动函数(hash函数)来降低哈希冲突的概率,使数据分布的更加平均
  3. 引入红黑树进一步降低遍历的时间复杂度,是遍历的更快

6.说一说HashMap什么时候会进行扩容?扩容多大

HashMap有个参数叫负载因子,其实就是一个小数值0.75,例如map的默认大小为16,当填满75%的空间就该扩容了,也就是说存到12个就该进行提前扩容了。

7.谈谈你对红黑树的见解?

  1. 每个节点非红即黑
  2. 根节点总是黑色的
  3. 如果节点是红色的,则它的子节点必须是黑色的(反之则不一定)
  4. 每个叶子节点都是黑色的空节点
  5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)

8.链表过深问题为什么不用二叉查找树代替,而选择红黑树?为什么不一直使用红黑树?

  1. 选择红黑树是为了解决二叉查找树的缺陷,二叉查找树在特殊情况下会变成一条线性结构(这就跟原来使用链表结构一样了,造成很深的问题),遍历查找会非常慢。
  2. 而红黑树在插入新数据后可能需要通过左旋,右旋、变色这些操作来保持平衡,引入红黑树就是为了查找数据快,解决链表查询深度的问题
  3. 我们知道红黑树属于平衡二叉树,但是为了保持“平衡”是需要付出代价的,但是该代价所损耗的资源要比遍历线性链表要少
  4. 所以当长度大于8的时候,会使用红黑树,如果链表长度很短的话,根本不需要引入红黑树,引入反而会慢。

6. TreeMap集合

  1. TreeMap底层是红黑树结构。红黑树详解
  2. 依赖于自然排序或者是比较器排序,对键进行排序
  3. 如果键存储的是自定义对象,需要实现Comparable接口或者在创建TreeMap对象的时候给出比较器排序规则

7.ConcurrentHashMap面试题

1.ConcurrentHashMap1.7和1.8的区别;

  1. jdk1.7:分段锁Segment来进行实现的,Segment继承了ReentrantLock锁;
  2. jdk1.8:放弃了Segment臃肿的设计,采用volatile+Node+CAS+Synchronized来保证线程安全;

2.JDK1.7的ConcurrentHashMap的实现原理

  1. ConcurrentHashMap中采用了分段锁保证了线程安全
  2. 分段锁称为Segment,它即类似于HashMap的结构,即内部拥有一个Entry数组,
  3. 数组中的每个元素又是一个链表,同时又是一个ReentrantLock
  4. 因为Segment继承了ReentrantLock。
  5. ReentrantLock就是实现多线程锁的关键
  6. 所谓的分段锁就是将数据分成一段一段的存储,
  7. 然后给每一段数据配一把锁,
  8. 当一个线程占用锁访问其中一个段数据的时候,
  9. 其他段的数据也能被其他线程访问,能够实现真正的并发访问。

3.JDK1.8的ConcurrentHashMap的实现原理

  1. 抛弃了原有的 Segment 分段锁,
  2. 采用了volatile+CAS+ synchronized来保证并发安全性

4.HashMap和HashTable的区别

  1. 默认容量不同,扩容不同

  2. 线程安全性:HashTable 安全

  3. 效率不同:HashTable 要慢,因为加锁

  4. 对于HashMap来说,可以存放null键和null值,而HashTable则不可以。

5.可以使用 CocurrentHashMap 来代替 Hashtable 吗?

  1. 我们知道 Hashtable 是 synchronized 的,但是 ConcurrentHashMap 同步性能更好,因为它仅仅根据同步级别对 map 的一部分进行上锁
  2. ConcurrentHashMap 当然可以代替 HashTable,但是 HashTable 提供更强的线程安全性
  3. 它们都可以用于多线程的环境,但是当 Hashtable 的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。由于 ConcurrentHashMap 引入了分割(segmentation),不论它变得多么大,仅仅需要锁定 Map 的某个部分,其它的线程不需要等到迭代完成才能访问 Map。简而言之,在迭代的过程中,ConcurrentHashMap 仅仅锁定 Map 的某个部分,而 Hashtable 则会锁定整个 Map。

8.总结

Map集合是Java三大集合之一,常在开发中用到,也是面试常考题,希望读者多多阅读,多看源码了解更深层的原理。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值