一、Map是什么?
Map是Java中集合的两大分类之一,Java概念中的集合与数学中的集合意义不同,Java中的集合是存储一组数据的容器。而从存储数据的类型,集合又可以分为两大类:Collection(即单列集合)、Map(即双列集合)。
单列集合很容易理解,存储的便是一组数据,与数组类似,但数组长度不可变,集合的长度是可变的(ArrayList底层仍然是数组,扩容是通过创建一个新的更长的数组来实现,严格意义上来说只是对数组的封装、强化,但链表就不同了)。
双列即键值对,Map中将一对Key-Value看作一个整体,Map集合中存储的便是这种键值对。例如:Map<String,Integer>表示Key为String类型,Value为Integer类型的键值对集合,我们可以在其中存储例如
"张三":22,
"李四":33,
"王五":44
这样的一组数据,如果将前面的String类型视作人名,Integer类型视作票数,我们就可以用这样的一个集合来存储每个人的票选情况。
值得注意的是,Map中要求Key不可重复,Value可以重复,我们可以把Key看成一条数据的身份证,那它当然是需要独一无二的。此外以上的例子限制了Key-Value的类型,我们也可以不限制这两项的类型,这样默认采用的是Object,因此我们可以存储任意类型的键值对数据,这点根据实际使用的要求来设定。
二、Map的常用方法
-
void clear();
删除集合中所有数据,返回空集合。
-
boolean containsKey(Object key);
判断集合中是否存在某个key(1个或0个)。
-
boolean equals(Object o);
判断两个集合中是否相等。
-
V get(Object key);
根据key获取value,key不存在则会返回空值。
-
boolean isEmpty();
判断集合是否为空。
-
V put(K key, V value);
往集合中添加数据(键值对),如果key已存在,则修改为新的value并返回原本的value。
-
int size();
返回集合的长度。
-
void putAll(Map<? extends K, ? extends V> m);
(java8以后新增)一次性往集合中添加多条数据(将一个map中的数据添加到另一个Map中,等同于遍历后对每条数据执行put(key,value)操作)。
-
V remove(Object key);
根据key删除数据,并返回value,如果key不存在则返回null。
-
V replace(K key, V value);
(java8以后新增)根据key替换value,如果key不存在则返回null。
-
boolean containsValue(Object value);
判断集合中是否存在某个value(可能存在多个)。
-
Collection<V> values();
以一个新的集合返回map集合中所有的value。
三、Map的遍历方式
java8以后新增了一些遍历方法 ,此处将java8作为分界点。
java8以前主要通过增强for循环与迭代器来进行遍历(一般不考虑反射)。
迭代器:
1.keySet(不推荐,只能获取到key,还是得从map中查找)
Map<String, Integer> map;
//初始化map、添加数据
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
Integer value = map.get(key);
System.out.println("Key: " + key + ", Value: " + value);
}
2.entrySet(推荐)
Map<String, Integer> map;
//初始化map、添加数据
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Integer> entry = iterator.next();
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("Key: " + key + ", Value: " + value);
}
增强for循环:
3.entrySet
Map<String, Integer> map;
//初始化map、添加数据
for (Map.Entry<String, Integer> entry : map.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("Key: " + key + ", Value: " + value);
}
4.keySet
与第一种方法相同的原因,效率低不推荐,此处省略。
java8以后添加了stream流以及lambda表达式,因此产生了一些新的遍历方法,也可以结合keySet、entrySet使用,下面展示两种比较简洁的遍历方式。
5.直接使用forEach
map.forEach((key, value) -> {
System.out.println("Key: " + key + ", Value: " + value);
});
6.stream流
map.entrySet().stream().forEach(entry -> {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("Key: " + key + ", Value: " + value);
});
四、常见的Map集合
1.HashMap
这是最基本也是最常用的Map实现类,用于快速查找键值对,key可以为null,但只能有一条。
特点:线程不安全,无序。
底层实现:java8以前是hash表+链表,java8以后则是hash表+链表+红黑树,当链表过长以后转为红黑树。
2.LinkedHashMap
在HashMap的基础上,用双向链表连接所有数据,因此可以保证数据的有序性。
特点:线程不安全,有序。
底层实现:与HashMap类似,多了一个双向链表维护元素插入顺序。
3.ConcurrentHashMap
在HashMap的基础上,采用分段锁的技术来保证线程安全性,在高并发环境下使用。
特点:线程安全,无序。
底层实现:与HashMap大致相同。
4.Hashtable
是早期的Map实现类,与HashMap类似,但线程安全,由于是早期的API,为了线程安全导致开销很大,效率较低,且没有对key的空值进行特殊处理,因此key不能为空,不推荐使用。
特点:线程安全,无序。
底层实现:与HashMap大致相同。
5.TreeMap
由于底层是红黑树,因此可以轻松实现排序效果,可以根据key进行排序,实现了NavigableMap接口(是一个SortedMap的扩展接口)。
特点:有序,线程不安全。
底层实现:红黑树。
补充:虽然TreeMap与LinkedHashMap都是有序的,但TreeMap是根据元素的key进行了排序,而LinkedHashMap是能保留插入顺序这个概念上的有序,不能混为一谈。