一、前言

<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=黑体>能够类比 HashSetLinkedHashSet

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=黑体>具体步骤如下:

  1. <font face=黑体>通过 keyset() 办法获取 Map 中所有的键,并存储到一个 Set 汇合中;
  2. <font face=黑体>遍历 Set 汇合,失去每一个键;
  3. <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=黑体>EntryMap 接口中的外部接口,咱们曾经晓得,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=黑体>具体步骤如下:

  1. <font face=黑体>应用 Map 汇合中的 entrySet() 办法,把 Map 汇合中的所有 Entry 对象取出来,存储到一个 Set 汇合中。
  2. <font face=黑体>遍历蕴含键值对对象的 Set 汇合,获取每一个键值对对象。
  3. <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 的区别如下:

  1. <font face=黑体>键和值都不容许存储 null
  2. <font face=黑体>线程平安,速度慢。

<font face=黑体>HashTable 汇合 和 Vector 汇合一样,曾经被更先进的汇合(HashMapArrayList)所取代。

<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=黑体>实现步骤:

  1. <font face=黑体>获取一个字符串对象;
  2. <font face=黑体>创立一个 Map 汇合,键代表字符,值代表次数;
  3. <font face=黑体>遍历字符串失去每个字符;
  4. <font face=黑体>判断 Map 中是否有该键;
  5. <font face=黑体>如果没有,第一次呈现,存储次数为 1;如果有,则阐明曾经呈现过,获取到对应的值进行 ++,再次存储;
  6. <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,有须要的能够去下载。