Map
由key
得值;
JDK
中在java.util
包下提供了一组类库,用以实现集合这一数据存储结构,作为容器,用以存储、处理大量对象。这些由JDK
提供的集合主要围绕着Collection
接口和Map
接口实现,本节主要介绍由Map
接口实现的集合。
1、Map接口相关继承关系
Map
接口下,主要由HashMap
类、Hashtable
类、TreeMap
类等对其进行实现,它们都在java.util
包下,它们之间简单的继承关系图可以概括如上图:
说明:
java.util.Map
是一个泛型接口,该接口规范了用来存储键值对数据的集合的行为,主要声明了put(K key, V value)、containsKey(Object key)、containsValue(Object value)、get(Object key)、keySet()、entrySet()、remove(Object key)、size()、clear()
等方法。实现java.util.Map
接口的集合。
java.util.HashMap类、java.util.Hashtable类、java.util.TreeMap类均分别实现了java.util.Map接口。
2、Map接口
实现了java.util.Map
接口的集合用于保存具有映射关系的数据,即以键值对(key->value)
的方式来存储数据。因此在集合内部有两个集合,一个集合用于保存key(键),一个集合用于保存value(值),实现了java.util.Map接口的集合都具有如下图所示的基本结构:
java.util.Map接口常见的实现类有java.util.HashMap类、java.util.Hashtable类、java.util.TreeMap类等,它们的功能与用法几乎完全相同,只是内部实现不同。
java.util.Map
接口中的一些常用API
如下:
参考API: JDK 1.8
序号 | 返回值 | 方法 | 方法说明 |
---|---|---|---|
01 | void | clear() | 从这个映射中移除所有的映射(可选操作)。 |
02 | default V | compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) | 试图计算出指定键和当前的映射值的映射(或 null如果没有当前映射)。 |
03 | default V | computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) | 如果指定的键是不是已经与价值相关的(或映射到 null),尝试使用给定的映射功能,进入到这个Map除非 null计算其价值。 |
04 | default V | computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction) | 如果指定键的值是存在和非空的,尝试计算一个新的映射,给出了键和它当前的映射值。 |
05 | boolean | containsKey(Object key) | 返回 true如果这Map包含一个指定的键映射。 |
06 | boolean | containsValue(Object value) | 返回 true如果映射到指定的值的一个或多个键。 |
07 | Set<Map.Entry<K,V>> | entrySet() | 返回一个 Set视图的映射包含在这个Map |
08 | boolean | equals(Object o) | 将指定的对象与此映射的相等性进行比较。 |
09 | default void | forEach(BiConsumer<? super K,? super V> action) | 在该映射中的每个条目执行给定的操作,直到所有的条目被处理或操作抛出异常。 |
10 | V | get(Object key) | 返回指定的键映射的值,或 null如果这个Map不包含的键映射 |
12 | default V | getOrDefault(Object key, V defaultValue) | 返回指定的键映射的值,或 defaultValue如果这个Map不包含的键映射。 |
13 | int | hashCode() | 返回此映射的哈希代码值。 |
14 | boolean | isEmpty() | 返回 true如果这个Map不包含键值的映射。 |
15 | Set<K> | keySet() | 返回一个 Set的关键视图包含在这个Map。 |
16 | default V | merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction) | 如果指定的键已与值相关联的值或与空值相关联的,则将其与给定的非空值关联。 |
17 | V | put(K key, V value) | 将指定的值与此映射中的指定键关联(可选操作)。 |
18 | void | putAll(Map<? extends K,? extends V> m) | 从指定的映射到这个Map(可选操作)复制所有的映射 |
19 | default V | putIfAbsent(K key, V value) | 如果指定的键是不是已经与价值相关的(或映射到 null)将其与给定的值并返回 null,否则返回当前值 |
20 | V | remove(Object key) | 如果存在(可选操作),则从该Map中移除一个键的映射 |
21 | default boolean | remove(Object key, Object value) | 仅当它当前映射到指定的值时,为指定的键移除条目。 |
22 | default V | replace(K key, V value) | 仅当它当前映射到某一值时,替换指定的键的条目 |
23 | default boolean | replace(K key, V oldValue, V newValue) | 仅当当前映射到指定的值时,替换指定的键的条目 |
24 | default void | replaceAll(BiFunction<? super K,? super V,? extends V> function) | 将每个条目的值替换为在该项上调用给定函数的结果,直到所有的条目都被处理或函数抛出异常 |
25 | int | size() | 返回这个映射中的键值映射的数目 |
26 | Collection | values() | 返回一个 Collection视图的值包含在这个Map。 |
2.1、HashMap类
java.util.HashMap
类借助散列算法实现java.util.Map
接口,该类是一个泛型类。java.util.HashMap
集合在保存键值映射数据时,键(key
)无序(这里的无序指元素不具有下标,无法通过下标索引来访问元素)且不可重复,不可重复这一特点由作为键的对象的hashCode()
方法和equals(Object obj)
方法保证。
下面是一个示例:
Person类的源码:
package com.codeke.java.test;
public class Person {
private String id; // 编号
private String name; // 名称
// 构造方法
public Person(String id, String name) {
this.id = id;
this.name = name;
}
// 获得编号
public String getId() {
return id;
}
// 获得名称
public String getName() {
return name;
}
}
测试类Test类的源码:
package com.codeke.java.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
// 声明Map类型的变量map,并赋值为一个HashMap实例
// 指定了该集合中key的类型参数为String,value的类型参数为Person
Map<String, Person> map = new HashMap<String, Person>();
// 实例化几个Person对象
Person p1 = new Person("001", "鲁智深");
Person p2 = new Person("002", "武松");
Person p3 = new Person("003", "林冲");
// 向map集合中添加元素
map.put(p1.getId(), p1);
map.put(p2.getId(), p2);
map.put(p3.getId(), p3);
// 通过key判断集合中是否包含"鲁智深"
if (map.containsKey(p1.getId())) {
System.out.println("通过键查找到了鲁智深");
} else {
System.out.println("通过键没有查找到了鲁智深");
}
// 通过value判断集合中是否包含"史进"
if (map.containsValue(p1)) {
System.out.println("通过值查找到了鲁智深");
} else {
System.out.println("通过值没有查找到了鲁智深");
}
// 从集合map中获取一个元素
Person person = map.get("001");
System.out.println("键为001的person,他的姓名是" + person.getName());
//迭代集合map中所有的对象
System.out.println("迭代集合中所有的元素");
// 返回集合map中键(key)所组成的Set集合
Set<String> set = map.keySet();
// 获取集合set的迭代器
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
Person p = map.get(key);
System.out.printf("键:%s 对应的person为: %s\n", key, p.getName());
}
// 从集合map中移除p1
map.remove(p1.getId());
// 显示移除p1后集合map中元素的个数
System.out.println("移除p1后集合中还有" + map.size() + "个元素");
}
}
执行输出结果:
通过键查找到了鲁智深
通过值查找到了鲁智深
键为001的person,他的姓名是鲁智深
迭代集合中所有的元素
键:001 对应的person为: 鲁智深
键:002 对应的person为: 武松
键:003 对应的person为: 林冲
移除p1后集合中还有2个元素
说明:
本例演示了如何实例化java.util.HashMap集合及如何使用Map集合的put(K key, V value)、containsKey(Object key)、get(Object key)、keySet()、remove(Object key)、size()等方法。
注意,Map集合的遍历需要借助Set集合和迭代器java.util.Iterator。
下面是另一个示例:
Person类的源码同上例。
测试类Test类的源码:
package com.codeke.java.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
// 声明Map类型的变量map,并赋值为一个HashMap实例
// 指定了该集合中key的类型参数为String,value的类型参数为Person
Map<String, Person> map = new HashMap<String, Person>();
// 实例化几个Person对象
Person p1 = new Person("001", "吴用");
Person p2 = new Person("002", "公孙胜");
Person p3 = new Person("003", "花荣");
// 向map集合中添加元素
map.put("001", p1);
map.put("002", p2);
map.put("002", p3); // 添加key重复的元素
// 返回集合map中所有键值映射所组成的Set集合
Set<Map.Entry<String, Person>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Person>> iterator = entrySet.iterator();
while (iterator.hasNext()){
Map.Entry<String, Person> entry = iterator.next();
String key = entry.getKey();
Person p = entry.getValue();
System.out.printf("键:%s 对应的person为: %s\n", key, p.getName());
}
}
}
执行输出结果:
键:001 对应的person为: 吴用
键:002 对应的person为: 花荣
说明:
实现了java.util.Map接口的集合中键是唯一的,但观察本例中的代码,在将对象p2和p3作为值(value)添加至集合时,所对应的键(key)均为002,这种情况下,后添加入集合的键值映射会覆盖之前存在的键值映射。
本例中还演示了Map集合的entrySet()方法,该方法可以返回集合中所有键值映射所组成的Set集合,该Set集合中的元素即是一对一对的键值映射数据。
java.util.Map接口中有一个内部接口Entry,该接口的实现类可以保存一对键值映射数据,即一个键(key)和对应的一个值(value),该接口中声明的最常用的方法是getKey()方法和getValue()方法,顾名思义,getKey()方法用以获取键(key),getValue()方法用以获取值(value)。
2.2、Hashtable类
java.util.Hashtable
类同java.util.HashMap
类非常相似,同样借助散列算法实现java.util.Map
接口,该类也是一个泛型类;与java.util.HashMap
类最核心的不同是,java.util.Hashtable
类是线程同步(Thread Synchronized
)的,所以它也是线程安全的,而java.util.HashMap
类是线程异步(Thread ASynchronized
)的,在多线程的场景下不能保证线程安全。
下面是一个示例:
Person类的源码同上例。
测试类Test类的源码:
package com.codeke.java.test;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
// 声明Map类型的变量map,并赋值为一个Hashtable实例
// 指定了该集合中key的类型参数为String,value的类型参数为Person
Map<String, Person> map = new Hashtable<String, Person>();
// 实例化几个Person对象
Person p1 = new Person("001", "鲁智深");
Person p2 = new Person("002", "武松");
Person p3 = new Person("003", "林冲");
// 向map集合中添加元素
map.put(p1.getId(), p1);
map.put(p2.getId(), p2);
map.put(p3.getId(), p3);
// 从集合map中获取一个元素
Person person = map.get("003");
System.out.println("键为003的person,他的姓名是" + person.getName());
//迭代集合map中所有的对象
System.out.println("迭代集合中所有的元素");
// 返回集合map中所有键值映射所组成的Set集合
Set<Map.Entry<String, Person>> entrySet = map.entrySet();
// 用加强型For循环遍历entrySet集合
for (Map.Entry<String, Person> entry : entrySet) {
String key = entry.getKey();
Person p = entry.getValue();
System.out.printf("键:%s 对应的person为: %s\n", key, p.getName());
}
// 从集合map中移除p1
map.remove(p1.getId());
// 显示移除p1后集合map中元素的个数
System.out.println("移除p1后集合中还有" + map.size() + "个元素");
}
}
执行输出结果:
键为003的person,他的姓名是林冲
迭代集合中所有的元素
键:003 对应的person为: 林冲
键:002 对应的person为: 武松
键:001 对应的person为: 鲁智深
移除p1后集合中还有2个元素
说明:
java.util.Hashtable类的使用与java.util.HashMap类使用方法几乎完全相同,这里不再赘述。
2.3、TreeMap类
java.util.TreeMap
类借助二叉树实现java.util.Map
接口,该类也是一个泛型类。该类的使用方法与java.util.HashMap
类几乎完全相同,不同的是,java.util.TreeMap
集合在保存键值映射数据时会对数据按照键(key
)进行比较,并按照键的数据类型所实现的比较方式(实现java.lang.Comparable
接口)对元素进行排序。
下面是一个示例:
Person类的源码同上例。
测试类Test类的源码:
package com.codeke.java.test;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
// 声明Map类型的变量map,并赋值为一个TreeMap实例
// 指定了该集合中key的类型参数为String,value的类型参数为Person
Map<String, Person> map = new TreeMap<String, Person>();
// 实例化几个Person对象
Person p1 = new Person("001", "鲁智深");
Person p2 = new Person("002", "武松");
Person p3 = new Person("003", "林冲");
Person p4 = new Person("004", "吴用");
Person p5 = new Person("005", "公孙胜");
Person p6 = new Person("006", "史进");
Person p7 = new Person("007", "花荣");
Person p8 = new Person("008", "柴进");
// 向map集合中添加元素
map.put(p5.getId(), p5);
map.put(p2.getId(), p2);
map.put(p1.getId(), p1);
map.put(p8.getId(), p8);
map.put(p6.getId(), p6);
map.put(p3.getId(), p3);
map.put(p7.getId(), p7);
map.put(p4.getId(), p4);
// 返回集合map中所有键值映射所组成的Set集合
Set<Map.Entry<String, Person>> entrySet = map.entrySet();
// 用加强型For循环遍历entrySet集合
for (Map.Entry<String, Person> entry : entrySet) {
String key = entry.getKey();
Person p = entry.getValue();
System.out.printf("键:%s 对应的person为: %s\n", key, p.getName());
}
}
}
执行输出结果:
键:001 对应的person为: 鲁智深
键:002 对应的person为: 武松
键:003 对应的person为: 林冲
键:004 对应的person为: 吴用
键:005 对应的person为: 公孙胜
键:006 对应的person为: 史进
键:007 对应的person为: 花荣
键:008 对应的person为: 柴进
说明:
观察本例的代码和执行输出结果,可以看到集合map
中的键值映射数据已经按照键(key
)进行了排序。