本文将为大家详细讲解Java中的Map集合,这是我们进行开发时经常用到的知识点,也是大家在学习Java中很重要的一个知识点,更是我们在面试时有可能会问到的问题。
文章较长,干货满满,建议大家收藏慢慢学习。文末有本文重点总结,主页有全系列文章分享。技术类问题,欢迎大家和我们一起交流讨论!
前言
在上一篇文章中给大家讲解了Java里的Set集合及其常用子类。
现在我们已经掌握了Java里的两大集合,最后还有另一大集合等待着我们学习,这就是Map集合。与之前的集合不太一样,Map集合属于双列集合,该集合中的信息是key-value形式;而之前的LIst和Set都是单列集合,里面的元素没有key。
有些小伙伴可能会很好奇,我们已经学习了List和Set集合了,为什么还要再搞出来一个Map集合呢?Map集合与Collection集合又有什么不同呢? 要想搞清楚以上问题,我们可以考虑这么一个需求。
假如我们利用List集合,来存储一份Student学生信息,要求你根据name来查找某个指定的学生分数,你要怎么实现?按照我们之前的经验,常用的方式就是遍历这个List,并挨个判断name是否相等,找到后就返回指定元素。
其实这种需求非常的常见,通过一个关键词来查询对应的值。如果我们使用List来实现该需求,效率会非常的低,因为平均需要扫描完集合的一半元素才能确定。而如果使用Map这种key-value键值对映射表的数据结构,就可以通过key快速地查找到value值。
全文大约【8500】 字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考…
一. Map集合
1. 简介
Map集合是一种以键值对形式存储和操作数据的数据结构,建立了key-value之间的映射关系,常用于存储和处理复杂的数据。同时Map也是一种双列集合接口,它有多个实现类,包括HashMap、TreeMap、LinkedHashMap
等,最常用的是HashMap
类。其中,HashMap是按哈希算法来实现存取键对象的,这是我们开发时最常用的Map子类,而TreeMap类则可以对键对象进行排序。
Map集合中的每个元素,都包含了一个键(key)和一个值(value),key和value组成了键-值的映射表,我们称其为键值对。键用于唯一标识一个元素,值用于存储该元素的数据,一般情况下,这个key和value可以是任何引用类型的数据。其中,键key是无序、无下标、不重复的,最多只能有一个key为null。值value是无序、无下标、可重复的,可以有多个value为null。
并且这个key和value之间是单向的一对一关系,即通过指定的key,总能找到唯一的、确定的value。当我们想从Map中取出数据时,只要给出指定的key,就能取出对应的value。
配套视频如下 :戳我直达视频
2. 特点
根据上面我们对Map概念的讲解,把Map的主要特点给大家总结如下:
- Map 和 List 不同,Map是一种双列集合;
- Map 存储的是 key-value 的映射关系;
- Map 不保证顺序 。在遍历时,遍历的顺序不一定是 put() 时放入的 key 的顺序,也不一定是 key 的排序顺序。
3. 实现方式
在Java中,Map集合的实现方式主要有两种:基于哈希表和基于树结构。接下来给大家简单介绍一下基于这两种结构的Map集合。
3.1 基于哈希表的Map集合
基于哈希表的Map,其底层是基于哈希表作为数据结构的集合,主要的实现类是HashMap
。
在HashMap
中,每个元素都包含一个键和一个值。当我们在添加元素时,HashMap会根据键的哈希值计算出对应的桶(Bucket),并将元素存储到该桶中。如果不同的键具有相同的哈希值,就会出现哈希冲突,此时HashMap会使用链表或红黑树等数据结构来解决冲突。基于哈希表的Map集合具有快速的添加、删除和查找元素的特点,但元素的顺序是不确定的。
3. 实现方式
在Java中,Map集合的实现方式主要有两种:基于哈希表和基于树结构。接下来壹哥给大家简单介绍一下基于这两种结构的Map集合。
3.1 基于哈希表的Map集合
基于哈希表的Map,其底层是基于哈希表作为数据结构的集合,主要的实现类是HashMap。在HashMap中,每个元素都包含一个键和一个值。当我们在添加元素时,HashMap会根据键的哈希值计算出对应的桶(Bucket),并将元素存储到该桶中。如果不同的键具有相同的哈希值,就会出现哈希冲突,此时HashMap会使用链表或红黑树等数据结构来解决冲突。基于哈希表的Map集合具有快速的添加、删除和查找元素的特点,但元素的顺序是不确定的。
3.2 基于树结构的Map集合
基于树结构的Map集合,其底层是基于红黑树作为数据结构的集合,主要的实现类是TreeMap。在TreeMap中,每个元素也都包含一个键和一个值。我们在添加元素时,TreeMap会根据键的比较结果,将元素存储到正确的位置上,使得元素可以按照键的升序或降序排列。基于树结构的Map集合,具有快速查找和遍历元素的特点,但添加和删除元素的速度较慢。
4. 常用方法
Map集合的使用和其他集合类似,主要包括添加、删除、获取、遍历元素等操作。
当我们调用put(K key, V value)方法时,会把key和value进行映射并放入Map。当调用V get(K key)时,可以通过key获取到对应的value;如果key不存在,则返回null。如果我们只是想查询某个key是否存在,可以调用containsKey(K key)方法。另外我们也可以通过 Map提供的keySet()方法,获取所有key组成的集合,进而遍历Map中所有的key-value对。
下表中就是Map里的一些常用方法,大家可以记一下,这些方法在Map的各实现子类中也都存在。
方法 | 描述 |
---|---|
clear() | 删除Map中所有的键-值对 |
isEmpty() | 判断Map是否为空 |
size() | 计算Map中键/值对的数量 |
put() | 将键/值对添加到Map中 |
putAll() | 将所有的键/值对添加到Map中 |
putIfAbsent() | 如果Map中不存在指定的键,则将指定的键/值对插入到Map中。 |
remove() | 删除Map中指定键key的映射关系 |
containsKey() | 检查Map中是否存在指定key对应的映射关系。 |
containsValue() | 检查Map中是否存在指定的 value对应的映射关系。 |
replace() | 替换Map中指定key对应的value。 |
replaceAll() | 将Map中所有的映射关系替换成给定函数执行的结果。 |
get() | 获取指定 key 对应对 value |
getOrDefault() | 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值 |
forEach() | 对Map中的每个映射执行指定的操作。 |
entrySet() | 返回Map中所有的键值对 |
keySet() | 返回Map中所有的key键 |
values() | 返回Map中所有的value值 |
merge() | 添加键值对到Map中 |
compute() | 对Map中指定key的值进行重新计算 |
computeIfAbsent() | 对Map中指定key的值进行重新计算,如果该key不存在,则添加到Map中 |
computeIfPresent() | 当key存在该Map时,对Map中指定key的值进行重新计算 |
与本节内容配套的视频链接如下: 戳我直达视频课程
5. 常用实现类
Java中有多个Map接口的实现类,接下来就给大家逐一简单介绍一下。
5.1 HashMap
HashMap是Java中最常用的Map集合实现类,它基于哈希表实现,具有快速查找键值对的优点。 HashMap的存储方式是无序的,也就是说,遍历HashMap集合时,得到的键值对顺序是不确定的。下面是创建HashMap集合的代码示例:
Map<String, Integer> hashMap = new HashMap<>();
5.2 TreeMap
TreeMap是Java中另一个常用的Map集合实现类,它基于红黑树实现,具有自动排序键值对的优点。TreeMap的存储方式是有序的,也就是说,遍历TreeMap集合时得到的键值对,是按照键的自然顺序或指定比较器的顺序排序的。下面是创建TreeMap集合的代码示例:
Map<String, Integer> treeMap = new TreeMap<>();
5.3 LinkedHashMap
LinkedHashMap是Java中另一个Map集合实现类,它继承自HashMap,并保持了插入顺序。也就是说,遍历LinkedHashMap集合时,得到的键值对的顺序是按照插入顺序排序的。下面是创建LinkedHashMap集合的代码示例:
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
5.4 Hashtable
Hashtable是Java中另一个Map集合实现类,它与HashMap非常相似,但Hashtable是线程安全的。Hashtable的存储方式是无序的,也就是说,遍历Hashtable集合时,得到的键值对的顺序是不确定的。下面是创建Hashtable集合的代码示例:
Map<String, Integer> hashtable = new Hashtable<>();
需要注意的是,由于Hashtable是线程安全的,所以在多线程环境中使用Hashtable的性能可能会较低,现在一般是建议使用ConcurrentHashMap。
5.5 ConcurrentHashMap
ConcurrentHashMap是Java中的另一个Map集合实现类,它与Hashtable非常相似,但是ConcurrentHashMap是线程安全的,并且性能更高。ConcurrentHashMap的存储方式是无序的,也就是说,遍历ConcurrentHashMap集合时,得到的键值对的顺序是不确定的。下面是创建ConcurrentHashMap集合的代码示例:
Map<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
需要注意的是,虽然ConcurrentHashMap是线程安全的,但仍然需要注意多线程环境下的并发问题。
二. HashMap集合
1. 简介
HashMap继承自AbstractMap类,实现了Map、Cloneable、java.io.Serializable接口,如下图所示:
HashMap是基于散列表实现的双列集合,它存储的是key-value键值对映射,每个key-value键值对也被称为一条Entry条目。其中的 key与 value,可以是任意的数据类型,其类型可以相同也可以不同。但一般情况下,key都是String类型,有时候也可以使用Integer类型;value可以是任何类型。并且在HashMap中,最多只能有一个记录的key为null,但可以有多个value的值为null。
HashMap中这些键值对(Entry)会分散存储在一个数组当中,这个数组就是HashMap的主体。 默认情况下,HashMap这个数组中的每个元素的初始值都是null。但HashMap中最多只允许一条记录的key为null,允许多条记录的value为null。
另外HashMap是非线程安全的,即任一时刻如果有多个线程同时对HashMap进行写操作,有可能会导致数据的不一致。 如果需要满足线程的安全性要求,可以用 Collections.synchronizedMap() 方法使得HashMap具有线程安全的能力,或者使用ConcurrentHashMap来替代。
HashMap实现了Map接口,会根据key的hashCode值存储数据,具有较快的访问速度。另外HashMap是无序的,不会记录插入元素的顺序。我们一般是根据key的hashCode值来存取数据, 大多数情况下都可以直接根据key来定位到它所对应的值,因而HashMap有较快的访问速度,但遍历顺序却不确定。
2. 特点
根据上面的描述,把HashMap的核心特点总结如下:
- 基于哈希表实现,具有快速查找键值对的优点;
- HashMap的存储方式是无序的;
- HashMap的key-value可以是任意类型,但key一般都是String类型,value类型任意;
- HashMap最多只能有一个记录的key为null,但可以有多个value为null。
3. 常用操作
HashMap的使用方法和其他Map集合类似,主要包括添加元素、删除元素、获取元素、遍历元素等操作。接下来会详细地给大家介绍HashMap集合的常用操作。
3.1 创建Map集合对象
创建HashMap对象的方式有多种,我们可以使用构造函数,代码如下:
// 使用构造函数创建HashMap对象
Map<String, Integer> map1 = new HashMap<>();
3.2 添加元素
向HashMap集合添加元素的方式和List集合类似,也是使用put()方法,下面是向HashMap集合中添加元素的代码示例:
public