一、前言
<font face= 黑体 > 汇合依照其存储构造能够分为两大类,别离是单列汇合 java.util.Collection
和双列汇合 java.util.Map
,单列汇合咱们曾经讲了,明天咱们来讲双列汇合 Map
。
二、Map 汇合
2.1、Map 汇合概述
<font face= 黑体 > 现实生活中,咱们常会看到这样的一种汇合:学生与学号,身份证号与集体,这种一一对应的关系,就叫做映射。Java
提供了专门的汇合类用来寄存这种对象关系,即 java.util.Map
接口。咱们来看一下 Map
汇合和 Collection
汇合的区别:
- <font face= 黑体 >
Collection
中的汇合,元素是孤立存在的,向汇合中存储元素采纳一个个元素的形式存储。 - <font face= 黑体 >
Map
中的汇合,元素是成对存在的。每个元素由键与值两局部组成,通过键能够找到对应的值。 - <font face= 黑体 >
Collection
中的汇合称为单列汇合,Map
中的汇合称为双列汇合。 - <font face= 黑体 > 须要留神的是,
Map
中的汇合不能蕴含反复的键,值能够反复,每个键只能对应一个值。
<font face= 黑体 >Map 接口中的汇合都有两个泛型变量 <K,V>,在应用时,要为两个泛型变量赋予数据类型。两个泛型变量 <K,V> 的数据类型能够雷同,也能够不同。
2.2、Map 接口罕用实现类
<font face= 黑体 >Map
有多个子类,这里咱们次要解说罕用的 HashMap
汇合和 LinkedHashMap
汇合。
- <font face= 黑体 >
HashMap<K,V>
:存储数据采纳的是哈希表构造,元素的存取程序有可能不统一(无序)。因为要保障键的惟一、不反复,须要重写键的hashCode()
办法和equals()
办法。 - <font face= 黑体 >
LinkedHashMap<K,V>
:HashMap
下有个子类LinkedHashMap
,存储数据采纳的是哈希表构造 + 链表构造。通过链表构造能够保障元素的存取程序统一,通过哈希表构造能够保障键的惟一、不反复,须要重写键的hashCode()
办法和equals()
办法。
<font face= 黑体 > 能够类比 HashSet 和 LinkedHashSet。
2.3、Map 接口中的罕用办法
<font face= 黑体 >Map
接口中定义了很多办法,罕用的如下:
-
<font face= 黑体 >
public V put(K key, V value)
: 把指定的键与指定的值增加到Map
汇合中,存储键值对的时候如果key
不反复,返回值v
是null
,如果key
反复,返回值v
是 被替换的value
值。private static void show01() { // 创立 Map 汇合 Map<String, String> map = new HashMap<>(); String v1 = map.put("至尊宝", "晶晶"); System.out.println(v1); // 返回 null // 这时候 晶晶 会被 紫霞仙子 替换,返回被替换的 value 值 晶晶 String v2 = map.put("至尊宝", "紫霞仙子"); System.out.println(v2); // 返回 晶晶 System.out.println(map); // 返回 {至尊宝 = 紫霞仙子} // key 不容许反复,value 能够反复 map.put("孙悟空", "紫霞仙子"); System.out.println(map); // 返回 {至尊宝 = 紫霞仙子, 孙悟空 = 紫霞仙子} }
-
<font face= 黑体 >
public V remove(Object key)
: 将指定的键所对应的元素在Map
汇合中删除,返回被删除元素的值。private static void show02() {Map<String, Integer> map = new HashMap<>(); map.put("王祖贤", 175); map.put("林青霞", 168); map.put("李嘉欣", 172); System.out.println(map); // 返回 {林青霞 =168, 李嘉欣 =172, 王祖贤 =175} Integer v1 = map.remove("李嘉欣"); System.out.println(v1); // 返回 172 System.out.println(map); // 返回 {林青霞 =168, 王祖贤 =175} Integer v2 = map.remove("李嘉诚"); System.out.println(v2); // 返回 null System.out.println(map); // 返回 {林青霞 =168, 王祖贤 =175} }
-
<font face= 黑体 >
public V get(Object key)
依据指定的键,在Map
汇合中获取对应的值。private static void show03() {Map<String, String> map = new HashMap<>(); map.put("邓超", "孙俪"); map.put("吴奇隆", "刘诗诗"); map.put("黄晓明", "杨颖"); String v1 = map.get("吴奇隆"); // 返回 刘诗诗 System.out.println(v1); String v2 = map.get("胡歌"); // 返回 null System.out.println(v2); }
-
<font face= 黑体 >
boolean containsKey(Object key)
判断汇合中是否蕴含指定的键。private static void show04() {Map<String, String> map = new HashMap<>(); map.put("邓超", "孙俪"); map.put("吴奇隆", "刘诗诗"); map.put("黄晓明", "杨颖"); System.out.println(map.containsKey("邓超")); // 返回 true System.out.println(map.containsKey("胡歌")); // 返回 false }
- <font face= 黑体 >
public Set<K> keySet()
: 获取Map
汇合中所有的键,存储到Set
汇合中。(遍历汇合时具体解说) - <font face= 黑体 >
public Set<Map.Entry<K,V>> entrySet()
: 获取到Map
汇合中所有的键值对对象并存储到Set
汇合中。(遍历汇合时具体解说)
2.4、Map 汇合的遍历
2.4.1、通过 keySet() 办法遍历 Map 汇合
<font face= 黑体 > 通过 keySet()
办法遍历 Map
汇合也称键找值形式:即通过元素中的键,获取键所对应的值。
<font face= 黑体 > 具体步骤如下:
- <font face= 黑体 > 通过
keyset()
办法获取Map
中所有的键,并存储到一个Set
汇合中; - <font face= 黑体 > 遍历
Set
汇合,失去每一个键; - <font face= 黑体 > 通过
get(K key)
办法,获取键所对应的值。
<font face= 黑体 > 图示如下:
<font face= 黑体 > 通过 keySet() 办法遍历 Map 汇合代码演示如下所示:
public class MapDemo02 {public static void main(String[] args) {Map<String, String> map = new HashMap<>();
map.put("邓超", "孙俪");
map.put("吴奇隆", "刘诗诗");
map.put("黄晓明", "杨颖");
// 1、通过 keyset() 办法获取 Map 中所有的键,并存储到一个 Set 汇合中;Set<String> set = map.keySet();
// 2、遍历 Set 汇合,失去每一个键;Iterator<String> it = set.iterator();
while (it.hasNext()) {String key = it.next();
// 3、通过 get(K key) 办法,获取键所对应的值。String value = map.get(key);
System.out.println(key + "=" + value);
}
System.out.println();
System.out.println("--------------------- 分割线 -----------------------");
System.out.println();
// 应用加强 for
for (String key : set) {String value = map.get(key);
System.out.println(key + "=" + value);
}
}
}
<font face= 黑体 > 应用加强 for 循环简化上述遍历代码:
public class MapDemo02 {public static void main(String[] args) {Map<String, String> map = new HashMap<>();
map.put("邓超", "孙俪");
map.put("吴奇隆", "刘诗诗");
map.put("黄晓明", "杨颖");
for (String key : map.keySet()) {String value = map.get(key);
System.out.println(key + "=" + value);
}
}
}
2.4.2、Entry 键值对对象
<font face= 黑体 >Entry
是 Map
接口中的外部接口, 咱们曾经晓得,Map
中寄存的是两种对象,一种称为 key
(键),一种称为 value
(值),它们在Map
中是一一对应关系,这一对象又称做 Map
中的一个 Entry(项)
。Entry
将键值对的对应关系封装成了对象。即键值对对象,这样咱们在遍历 Map
汇合时,就能够从每一个键值对(Entry
)对象中获取对应的键与对应的值。
<font face= 黑体 >Entry
示意一对键和值,提供了获取对应键和对应值的办法:
- <font face= 黑体 >
public K getKey()
:获取Entry
对象中的键。 - <font face= 黑体 >
public V getValue()
:获取Entry
对象中的值。
2.4.3、通过 entrySet() 办法遍历 Map 汇合
<font face= 黑体 > 在 Map
汇合中提供了获取所有 Entry
对象的办法:
- <font face= 黑体 >
public Set<Map.Entry<K,V>> entrySet()
: 获取Map
汇合中所有的键值对对象并存储到Set
汇合中。
<font face= 黑体 > 通过 entrySet()
办法遍历 Map
汇合也称 键值对形式:即通过汇合中每个键值对 (Entry
) 对象,获取键值对对象中的键与值。
<font face= 黑体 > 具体步骤如下:
- <font face= 黑体 > 应用
Map
汇合中的entrySet()
办法,把Map
汇合中的所有Entry
对象取出来,存储到一个Set
汇合中。 - <font face= 黑体 > 遍历蕴含键值对对象的
Set
汇合,获取每一个键值对对象。 - <font face= 黑体 > 通过键值对对象的
getKey()
办法和getValue()
办法获取键与值。
<font face= 黑体 > 通过 entrySet() 办法遍历 Map 汇合代码演示如下所示:
public class MapDemo03 {public static void main(String[] args) {Map<String, String> map = new HashMap<>();
map.put("邓超", "孙俪");
map.put("吴奇隆", "刘诗诗");
map.put("黄晓明", "杨颖");
// 1、应用 Map 汇合中的 entrySet() 办法,把 Map 汇合中的所有 Entry 对象取出来,存储到一个 Set 汇合中
Set<Map.Entry<String, String>> set = map.entrySet();
// 2、遍历蕴含键值对对象的 Set 汇合,获取每一个键值对对象
Iterator<Map.Entry<String, String>> it = set.iterator();
while (it.hasNext()) {Map.Entry<String, String> entry = it.next();
// 3、通过键值对对象的 `getKey()` 办法和 `getValue()` 办法获取键与值。String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
}
}
<font face= 黑体 > 应用加强 for 循环简化上述遍历代码:
public class MapDemo03 {public static void main(String[] args) {Map<String, String> map = new HashMap<>();
map.put("邓超", "孙俪");
map.put("吴奇隆", "刘诗诗");
map.put("黄晓明", "杨颖");
for (Map.Entry<String, String> entry : map.entrySet()) {System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
}
2.5、HashMap 存储自定义类型的键值
<font face= 黑体 >Map
汇合要保障 key
是惟一的,即作为 key
的元素必须得重写 hashCode()
办法和 equals()
办法。
<font face= 黑体 >HashMap 存储自定义类型代码演示如下所示:
public class Student {
private String name;
private int age;
public Student() {}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
@Override
public int hashCode() {return Objects.hash(name, age);
}
@Override
public boolean equals(Object o) {if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {//show01();
show02();}
/**
* HashMap 存储自定义类型的键值
* key:String 曾经重写了 hashCode() 和 equals()
* value: 应用 Student value 能够反复(同名同年龄的人就视为反复)*/
private static void show01() {
// 创立 hashMap 汇合
HashMap<String, Student> map = new HashMap<String, Student>();
map.put("北京", new Student("张三", 18));
map.put("上海", new Student("李四", 19));
map.put("广州", new Student("王五", 20));
map.put("北京", new Student("赵六", 18));
// 应用 keySet() 遍历汇合
for (String key : map.keySet()) {System.out.println(key + "-->" + map.get(key));
}
}
/**
* HashMap 存储自定义类型的键值
* key: 应用 Student Student 必须重写 hashCode 和 equals 办法保障 key 惟一
* value: 应用 String
*/
private static void show02() {
// 创立 hashMap 汇合
HashMap<Student, String> map = new HashMap<Student, String>();
map.put(new Student("张三", 18), "北京");
map.put(new Student("李四", 19), "上海");
map.put(new Student("王五", 20), "广州");
map.put(new Student("张三", 18), "深圳");
// 应用 entrySet() 遍历汇合
for (Map.Entry<Student, String> entry : map.entrySet()) {System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
}
<font face= 黑体 > 打印后果如下所示,因为 String 类和 Student 类都重写了 hashCode() 办法和 equals() 办法,所以存储雷同的 key 的时候,前面的 value 就会替换后面的 value,即 “ 赵六 ” 会替换 “ 张三 ”,” 深圳 ” 会替换 “ 北京 ”。
2.5、LinkedHashMap 汇合
<font face= 黑体 > 在 HashMap
上面有一个子类 LinkedHashMap
,它是链表和哈希表组合的一个数据存储构造,它是有序的。
<font face= 黑体 >LinkedHashMap 汇合代码演示如下所示:
public class LinkedHashMapDemo01 {public static void main(String[] args) {HashMap<String, String> map = new HashMap<>();
map.put("a", "a");
map.put("c", "c");
map.put("b", "b");
map.put("d", "d");
System.out.println(map); // 返回 {a=a, b=b, c=c, d=d}
LinkedHashMap<String, String> map1 = new LinkedHashMap<>();
map1.put("a", "a");
map1.put("c", "c");
map1.put("b", "b");
map1.put("d", "d");
System.out.println(map1); // 返回 {a=a, c=c, b=b, d=d}
}
}
2.6、HashTable 汇合
<font face= 黑体 >HashTable
也是继承自 Map
接口的,底层也是一个哈希表,跟 HashMap
的区别如下:
- <font face= 黑体 > 键和值都不容许存储
null
。 - <font face= 黑体 > 线程平安,速度慢。
<font face= 黑体 >HashTable
汇合 和 Vector
汇合一样,曾经被更先进的汇合(HashMap
,ArrayList
)所取代。
<font face= 黑体 >HashTable 汇合代码演示如下所示:
public class HashTableDemo01 {public static void main(String[] args) {HashMap<String, String> map = new HashMap<String, String>();
map.put(null, "a");
map.put("b", null);
map.put(null, null);
System.out.println(map); // 返回 {null=null, b=null}
Hashtable<String, String> map2 = new Hashtable<String, String>();
map2.put(null, "a"); // 报错 NullPointerException
map2.put("b", null); // 报错 NullPointerException
map2.put(null, null); // 报错 NullPointerException
}
}
三、Map 汇合练习
<font face= 黑体 > 需要:
<font face= 黑体 > 计算一个字符串中每个字符呈现的次数。
<font face= 黑体 > 实现步骤:
- <font face= 黑体 > 获取一个字符串对象;
- <font face= 黑体 > 创立一个
Map
汇合,键代表字符,值代表次数; - <font face= 黑体 > 遍历字符串失去每个字符;
- <font face= 黑体 > 判断
Map
中是否有该键; - <font face= 黑体 > 如果没有,第一次呈现,存储次数为 1;如果有,则阐明曾经呈现过,获取到对应的值进行 ++,再次存储;
- <font face= 黑体 > 打印最终后果。
<font face= 黑体 > 代码实现:
public class ExampleDemo01 {public static void main(String[] args) {
// 1、应用 Scanner 获取一个字符串对象;Scanner sc = new Scanner(System.in);
System.out.println("请输出一个字符串:");
String str = sc.next();
// 2、创立一个 Map 汇合,键代表字符,值代表次数;HashMap<Character, Integer> map = new HashMap();
// 3、遍历字符串失去每个字符;for (char c : str.toCharArray()) {
// 4、判断 Map 中是否有该键;if (map.containsKey(c)) {Integer value = map.get(c);
value++;
map.put(c, value);
} else {map.put(c, 1);
}
}
// 5、打印最终后果。for (Character key : map.keySet()) {System.out.println(key + "-->" + map.get(key));
}
}
}
四、源码
<font face= 黑体 > 文章中用到的所有源码已上传至 github,有须要的能够去下载。