8.6 Java 8增强的Map集合

8.6 Java 8增强的Map集合

Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value,key和value都可以是任何引用类型的数据。

Map的key不可重复

Map的key不允许重复,即同一个Map对象的任何两个key通过equals()方法比较总是返回false

一个key对应一个value

key和value之间存在单向一对一关系,即通过指定的key,总能找到唯一的、确定的value。

根据key来获取value

从Map中取出数据时,只要给出指定的key,就可以取出对应的value。

如果把Map的两组值拆开来看,Map里的数据有如图8.7所示的结构。

这里有一张图片

Map中的所有key可以看成一个Set集合

从图8.7中可以看出,如果把Map里的所有key放在一起来看,它们就组成了一个Set集合(所有的key没有顺序,key与key之间不能重复)。

keySet()方法

实际上Map确实包含了一个keySet()方法,用于返回Map里所有key组成的Set集合。

方法 描述
Set<K> keySet() Returns a Set view of the keys contained in this map.

不仅如此,Map里key集和Set集合里元素的存储形式也很像,Map子类和Set子类在名字上也惊人地相似,比如Set接口下有HashSet、 LinkedHashSet、 SortedSet(接口)、 TreeSet、 EnumSet等子接口和实现类,而Map接口下则有HashMap、 LinkedHashMap、 SortedMap(接口)、 TreeMap、 EnumMap等子接口和实现类。正如它们的名字所暗示的,Map的这些实现类和子接口中key集的存储形式和对应Set集合中元素的存储形式完全相同。

提示

Set与Map之间的关系非常密切。虽然Map中放的元素是key-value对,Set集合中放的元素是单个对象,但如果把key-value对中的value当成key的附庸:key在哪里, value就跟在哪里。这样就可以像对待Set一样来对待Map了。事实上,Map提供了一个Entry内部类来封装key-value对,而计算Entry存储时则只考虑Entry封装的key。

Map接口的内部接口Entry 描述
static interface Map.Entry<K,​V> A map entry (key-value pair).

Java使用Map来实现Set

从Java源码来看,Java是先实现了Map,然后通过包装一个所有value都为null的Map就实现了Set集合。

Map中的的所有value可以看成一个List集合

如果把Map里的所有value放在一起来看,它们又非常类似于一个List:

  • 元素与元素之间可以重复,每个元素可以根据索引来查找,只是Map中的索引不再使用整数值,而是以另一个对象作为索引。
  • 如果需要从List集合中取出元素,则需要提供该元素的数字索引;
  • 如果需要从Map中取出元素,则需要提供该元素的key索引。

因此,Map有时也被称为字典,或关联数组。

Map接口方法

Map接口中定义了如下常用方法。

添加元素的方法

方法 描述
V put(K key, V value) 添加一个key-value对,如果当前Map中已有一个与该key相等的key-value对,则新的key-value对会覆盖原来的key-value对。
void putAll(Map<? extends K,? extends V> m) 将指定Map中的key-value对复制到当前Map中。

查询方法

方法 描述
Object get(Object key) 返回指定key所对应的value;如果此Map中不包含该key,则返回null。
int size() 返回该Map里的key-value对的个数。

删除元素的方法

方法 描述
V remove(Object key) 删除指定key所对应的key-value对,返回被删除key所关联的value,如果该key不存在,则返回null。
default boolean remove(Object key, Object value) 这是Java8新增的方法,删除指定key,value所对应的key-value对。如果从该Map中成功地删除该key-value对,该方法返回true,否则返回false。
void clear() 删除该Map对象中的所有key-value对.

判断方法

方法 描述
boolean isEmpty() 查询该Map是否为空(即不包含任何key-value对),如果为空则返回true。
boolean containsKey(object key) 查询Map中是否包含指定的key,如果包含则返回true
boolean containsValue(Object value) Returns true if this map maps one or more keys to the specified value.

返回集合

方法 描述
Set keySet() 返回该Map中所有key组成的Set集合。
Set entrySet() 返回Map中包含的key-value对所组成的Set集合,每个集合元素都是 Map.Entry对象(Entry是Map的内部类)。
Collection values() 返回该Map里所有 value组成的 Collection

Map的实现类

Map接口提供了大量的实现类,典型实现如HashMap和Hashtable等、 HashMap的子类LinkedHashMap,还有SortedMap子接口及该接口的实现类TreeMap,以及WeakHashMap、IdentityHashMap等。下面将详细介绍Map接口实现类。

Map的内部类Entry

Map中包括一个内部类Entry,该类封装了一个key-value对。 Entry包含如下三个方法。

Mapt.Entry内部接口的方法 描述
K getKey() 返回该Entry里包含的key值。
V getValue() 返回该Entry里包含的value值。
V setValue(V value) 设置该Entry里包含的value值,并返回新设置的value值。

Map集合最典型的用法就是成对地添加、删除 key-value对,接下来即可判断该Map中是否包含指定key,是否包含指定value,也可以通过Map提供的keySet()方法获取所有key组成的集合,进而遍历Map中所有的key-value对。

程序 Map接口基本用法

下面程序示范了Map的基本功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.util.*;

public class MapTest
{
public static void main(String[] args)
{
Map map = new HashMap();
// 成对放入多个key-value对
map.put("疯狂Java讲义" , 109);
map.put("疯狂iOS讲义" , 10);
map.put("疯狂Ajax讲义" , 79);
// 多次放入的key-value对中value可以重复
map.put("轻量级Java EE企业应用实战" , 99);
// 放入重复的key时,新的value会覆盖原有的value
// 如果新的value覆盖了原有的value,该方法返回被覆盖的value
System.out.println(map.put("疯狂iOS讲义" , 99)); // 输出10
System.out.println(map); // 输出的Map集合包含4个key-value对
// 判断是否包含指定key
System.out.println("是否包含值为 疯狂iOS讲义 key:"
+ map.containsKey("疯狂iOS讲义")); // 输出true
// 判断是否包含指定value
System.out.println("是否包含值为 99 value:"
+ map.containsValue(99)); // 输出true
// 获取Map集合的所有key组成的集合,通过遍历key来实现遍历所有key-value对
for (Object key : map.keySet() )
{
// map.get(key)方法获取指定key对应的value
System.out.println(key + "-->" + map.get(key));
}
map.remove("疯狂Ajax讲义"); // 根据key来删除key-value对。
System.out.println(map); // 输出结果不再包含 疯狂Ajax讲义=79 的key-value对
}
}

运行结果:

1
2
3
4
5
6
7
8
9
10
{疯狂Ajax讲义=79, 疯狂iOS讲义=99, 轻量级Java EE企业应用实战=99, 疯狂Java讲义=109}
是否包含值为 疯狂iOS讲义 key:true
是否包含值为 99 value:true
疯狂Ajax讲义-->79
疯狂iOS讲义-->99
轻量级Java EE企业应用实战-->99
疯狂Java讲义-->109
{疯狂iOS讲义=99, 轻量级Java EE企业应用实战=99, 疯狂Java讲义=109}

上面程序中先示范了向Map中成对地添加key-value对。添加key-value对时,Map允许多个value重复,但如果添加key-value对时Map中已有重复的key,那么新添加的value会覆盖该key原来对应的value并返回被覆盖的value。

程序接下来的代码分别判断了Map集合中是否包含指定的key、是否包含指定的value。

使用foreach循环 遍历Map集合

程序中的foreach循环用于遍历Map集合:

  • 先调用Map集合的keySet()获取包含所有key的Set集合,
  • 然后使用foreach循环来遍历这个Set集合中的key,
  • 最后根据key来获取对应的value。

所有的Map实现类都重写了toString()方法,调用Map对象的toString()方法总是返回如下格式的字符串:{key1=value,key2=value2…}.