JavaSE学习笔记(二十二)—— Map集合

一、Map集合概述

1.1 Map引入

  作为学生来说,是根据学号来区分不同的学生的,那么假设我现在已经知道了学生的学号,我要根据学号去获取学生姓名,请问怎么做呢?如果采用前面讲解过的集合,我们只能把学号和学生姓名作为一个对象的成员,然后存储整个对象,将来遍历的时候,判断,获取对应的名称。但是呢,如果我都能把学生姓名拿出来了,我还需要根据编号去找吗?
  针对我们目前的这种需求:仅仅知道学号,就想知道学生姓名的情况,Java就提供了一种新的集合 Map。

1.2 什么是Map

  通过查看API,我们知道Map集合的一个最大的特点,就是它可以存储键值对的元素。这个时候存储我们上面的需求,就可以这样做:

    学号1     姓名1
    学号2     姓名2
    学号3     姓名3
    学号2(不行)   姓名4
    学号4     姓名4

  Map集合的特点:将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。 

1.3 Map集合和Collection集合的区别

  • Map集合存储元素是成对出现的,Map集合的键是唯一的,值是可重复的。可以把这个理解为:夫妻对
  • Collection集合存储元素是单独出现的,Collection的儿子Set是唯一的,List是可重复的。可以把这个理解为:光棍
  • Map集合的数据结构只针对键有效,跟值无关
  • Collection集合的数据结构是针对元素有效

二、Map集合的功能

2.1 基本功能

【添加功能】

  V put(K key,V value):添加元素。如果键是第一次存储,就直接存储元素,返回null;如果键不是第一次存在,就用值把以前的值替换掉,返回以前的值。

【删除功能】

  void clear():移除所有的键值对元素
  V remove(Object key):根据键删除键值对元素,并把值返回

【判断功能】

  boolean containsKey(Object key):判断集合是否包含指定的键
  boolean containsValue(Object value):判断集合是否包含指定的值
  boolean isEmpty():判断集合是否为空

【获取功能】

  Set<Map.Entry<K,V>> entrySet():返回的是键值对对象的集合(为什么用Set,因为HashMap要确保key值的唯一性)
  V get(Object key):根据键获取值
  Set<K> keySet():获取集合中所有键的集合
  Collection<V> values():获取集合中所有值的集合

【长度功能】

  int size():返回集合中的键值对的对数

public class MapDemo {
    public static void main(String[] args) {
        // 创建Map集合
        Map<String, String> map = new HashMap<>();

        // 添加元素
        // V put(K key,V value):添加元素
        // System.out.println("put:" + map.put("文章", "马伊俐"));//null
        // System.out.println("put:" + map.put("文章", "姚笛"));//马伊俐

        map.put("邓超", "孙俪");
        map.put("黄晓明", "杨颖");
        map.put("周杰伦", "蔡依林");
        map.put("刘恺威", "杨幂");

        // void clear():移除所有的键值对元素
        // map.clear();

        // V remove(Object key):根据键删除键值对元素,并把值返回
        // System.out.println("remove:"+map.remove("邓超"));//孙俪

        // boolean containsKey(Object key):判断集合是否包含指定的键
        // System.out.println("containsKey:"+map.containsKey("黄晓明"));//true

        // boolean isEmpty():判断集合是否为空
        // System.out.println("isEmpty:" + map.isEmpty());

        //int size():返回集合中的键值对的对数
        // System.out.println("size:" + map.size());//4

        // V get(Object key):根据键获取值
        // System.out.println("get:"+map.get("周杰伦"));//蔡依林
        // System.out.println("get:"+map.get("周杰"));//null

        // Set<K> keySet():获取集合中所有键的集合
        Set<String> set = map.keySet();
        for (String key : set) {
            System.out.println(key);
        }
        System.out.println("-------------");

        // Collection<V> values():获取集合中所有值的集合
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }

        // 输出集合名称
        System.out.println("map:" + map);//map:{周杰伦=蔡依林, 刘恺威=杨幂, 邓超=孙俪, 黄晓明=杨颖}
    }
}

2.2 Map集合的遍历

【方式一】——map.keySet()

  Map -- 夫妻对
  思路:
    A:把所有的丈夫给集中起来。
    B:遍历丈夫的集合,获取得到每一个丈夫。
    C:让丈夫去找自己的妻子。

  转换:
    A:获取所有的键
    B:遍历键的集合,获取得到每一个键
    C:根据键去找值

public class MapDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<>();

        // 创建元素并添加到集合
        map.put("杨过", "小龙女");
        map.put("郭靖", "黄蓉");
        map.put("杨康", "穆念慈");
        map.put("陈玄风", "梅超风");

        // 遍历
        // 获取所有的键
        Set<String> set = map.keySet();
        // 遍历键的集合,获取得到每一个键
        for (String key : set) {
            // 根据键去找值
            String value = map.get(key);
            System.out.println(key + "---" + value);
        }
    }
}

【方式二】——map.entrySet()

  Map -- 夫妻对

  思路:
    A:获取所有结婚证的集合
    B:遍历结婚证的集合,得到每一个结婚证
    C:根据结婚证获取丈夫和妻子

  转换:  
    A:获取所有键值对对象的集合
    B:遍历键值对对象的集合,得到每一个键值对对象
    C:根据键值对对象获取键和值

public class MapDemo3 {
    public static void main(String[] args) {
        // 创建集合对象
        Map<String, String> map = new HashMap<String, String>();

        // 创建元素并添加到集合
        map.put("杨过", "小龙女");
        map.put("郭靖", "黄蓉");
        map.put("杨康", "穆念慈");
        map.put("陈玄风", "梅超风");

        // 获取所有键值对对象的集合
        Set<Map.Entry<String, String>> set = map.entrySet();
        // 遍历键值对对象的集合,得到每一个键值对对象
        for (Map.Entry<String, String> entry : set) {
            // 根据键值对对象获取键和值
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "---" + value);
        }
    }
}

【两种遍历方式的比较】

 

三、Map集合常用子类

3.1 HashMap

  HashMap是基于哈希表的Map接口实现。哈希表的作用是用来保证键的唯一性的。

  HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。

  

  从上图中可以看出,HashMap底层就是一个数组结构,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。

【HashMap<String,String>】

public class HashMapDemo {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<String, String> map = new HashMap<>();

        // 创建元素并添加元素
        map.put("it001", "马云");
        map.put("it003", "马化腾");
        map.put("it004", "乔布斯");
        map.put("it005", "张朝阳");
        map.put("it002", "裘伯君");
        map.put("it001", "比尔盖茨");

        // 遍历
        Set<String> set = map.keySet();
        for (String key : set) {
            String value = map.get(key);
            System.out.println(key + "---" + value);
        }
    }
}

【HashMap<Integer,String>】

public class HashMapDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<Integer, String> hm = new HashMap<Integer, String>();

        // 创建元素并添加元素
        hm.put(27, "林青霞");
        hm.put(30, "风清扬");
        hm.put(28, "刘意");
        hm.put(29, "林青霞");

        // 下面的写法是八进制,但是不能出现8以上的单个数据
        // hm.put(003, "hello");
        // hm.put(006, "hello");
        // hm.put(007, "hello");
        // hm.put(008, "hello");//报错

        // 遍历
        Set<Integer> set = hm.keySet();
        for (Integer key : set) {
            String value = hm.get(key);
            System.out.println(key + "---" + value);
        }

        // 下面这种方式仅仅是集合的元素的字符串表示
        // System.out.println("hm:" + hm);
    }
}

【HashMap<String,Student>】

public class MapDemo3 {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<String, Student> hm = new HashMap<>();

        // 创建学生对象
        Student s1 = new Student("周星驰", 58);
        Student s2 = new Student("刘德华", 55);
        Student s3 = new Student("梁朝伟", 54);
        Student s4 = new Student("刘嘉玲", 50);

        // 添加元素
        hm.put("9527", s1);
        hm.put("9522", s2);
        hm.put("9524", s3);
        hm.put("9529", s4);

        // 遍历
        Set<String> set = hm.keySet();
        for (String key : set) {
            Student s = hm.get(key);
            System.out.println(key + "---" + s.getName() + "---" + s.getAge());
        }
    }
}

【HashMap<Student,String>】

  键:Student
    要求:如果两个对象的成员变量值都相同,则为同一个对象。
  值:String

public class HashMapDemo4 {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<Student, String> hm = new HashMap<Student, String>();

        // 创建学生对象
        Student s1 = new Student("貂蝉", 27);
        Student s2 = new Student("王昭君", 30);
        Student s3 = new Student("西施", 33);
        Student s4 = new Student("杨玉环", 35);
        Student s5 = new Student("貂蝉", 27);

        // 添加元素
        hm.put(s1, "8888");
        hm.put(s2, "6666");
        hm.put(s3, "5555");
        hm.put(s4, "7777");
        hm.put(s5, "9999");

        // 遍历
        Set<Student> set = hm.keySet();
        for (Student key : set) {
            String value = hm.get(key);
            System.out.println(key.getName() + "---" + key.getAge() + "---"
                    + value);
        }
    }
}

  注意:此处Student类要重写equals()和hashCode()方法,不然在相同key的情况下,map无法完成覆盖。

3.2 LinkedHashMap

  LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。

  由哈希表保证键的唯一性
  由链表保证键盘的有序(存储和取出的顺序一致)

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        // 创建集合对象
        LinkedHashMap<String, String> map = new LinkedHashMap<>();

        // 创建并添加元素
        map.put("2345", "hello");
        map.put("1234", "world");
        map.put("3456", "java");
        map.put("1234", "javaee");
        map.put("3456", "android");

        // 遍历
        Set<String> set = map.keySet();
        for (String key : set) {
            String value = map.get(key);
            System.out.println(key + "---" + value);
        }
    }
}

3.3 TreeMap

  TreeMap是基于红黑树的Map接口的实现。

  可以保证键的排序和唯一性

【TreeMap<String,String>】

public class TreeMapDemo {
    public static void main(String[] args) {
        // 创建集合对象
        TreeMap<String, String> map = new TreeMap<>();

        // 创建元素并添加元素
        map.put("hello", "你好");
        map.put("world", "世界");
        map.put("java", "爪哇");
        map.put("world", "世界2");
        map.put("javaee", "爪哇EE");

        // 遍历集合
        Set<String> set = map.keySet();
        for (String key : set) {
            String value = map.get(key);
            System.out.println(key + "---" + value);
        }
    }
}

  可以看出结果是按照key的自然排序方式打印出来的,因为key是String类型,而String类型实现了Comparable接口。

【TreeMap<Student,String>】

public class TreeMapDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        TreeMap<Student, String> map = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                // 主要条件
                int num = s1.getAge() - s2.getAge();
                // 次要条件
                int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
                return num2;
            }
        });

        // 创建学生对象
        Student s1 = new Student("潘安", 30);
        Student s2 = new Student("柳下惠", 35);
        Student s3 = new Student("唐伯虎", 33);
        Student s4 = new Student("燕青", 32);
        Student s5 = new Student("唐伯虎", 33);

        // 存储元素
        map.put(s1, "宋朝");
        map.put(s2, "元朝");
        map.put(s3, "明朝");
        map.put(s4, "清朝");
        map.put(s5, "汉朝");

        // 遍历
        Set<Student> set = map.keySet();
        for (Student key : set) {
            String value = map.get(key);
            System.out.println(key.getName() + "---" + key.getAge() + "---"
                    + value);
        }
    }
}

  注意:当key是自定义对象时,对象类本身必须实现Comparable接口,让元素具有比较性;或者让集合的构造方法接收一个比较器接口的子类对象 Comparator,集合具备比较性。

  如果二者都不实现,将会报错:

四、Map集合案例

4.1 获取字符串每个字母的出现次数

   需求 :"aababcabcdabcde",获取字符串中每一个字母出现的次数要求结果:a(5)b(4)c(3)d(2)e(1)

/**
 * 分析:
 *         A:定义一个字符串(可以改进为键盘录入)
 *         B:定义一个TreeMap集合
 *             键:Character
 *             值:Integer
 *         C:把字符串转换为字符数组
 *         D:遍历字符数组,得到每一个字符
 *         E:拿刚才得到的字符作为键到集合中去找值,看返回值
 *             是null:说明该键不存在,就把该字符作为键,1作为值存储
 *             不是null:说明该键存在,就把值加1,然后重写存储该键和值
 *         F:定义字符串缓冲区变量
 *         G:遍历集合,得到键和值,进行按照要求拼接
 *         H:把字符串缓冲区转换为字符串输出
 */
public class TreeMapDemo {
    public static void main(String[] args) {
        // 定义一个字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String line = sc.nextLine();

        // 定义一个TreeMap集合
        TreeMap<Character, Integer> map = new TreeMap<>();

        //把字符串转换为字符数组
        char[] chs = line.toCharArray();

        //遍历字符数组,得到每一个字符
        for (char ch : chs) {
            //拿刚才得到的字符作为键到集合中去找值,看返回值
            Integer i = map.get(ch);

            //是null:说明该键不存在,就把该字符作为键,1作为值存储
            if (i == null) {
                map.put(ch, 1);
            } else {
                //不是null:说明该键存在,就把值加1,然后重写存储该键和值
                i++;
                map.put(ch, i);
            }
        }

        //定义字符串缓冲区变量
        StringBuilder sb = new StringBuilder();

        //遍历集合,得到键和值,进行按照要求拼接
        Set<Character> set = map.keySet();
        for (Character key : set) {
            Integer value = map.get(key);
            sb.append(key).append("(").append(value).append(")");
        }

        //把字符串缓冲区转换为字符串输出
        String result = sb.toString();
        System.out.println("result:" + result);
    }
}

4.2 集合的嵌套遍历

【HashMap嵌套HashMap】

  场景:

  传智播客
    jc   基础班
        陈玉楼 20
        高跃 22
    jy   就业班
        李杰 21
        曹石磊 23

public class HashMapDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<String, HashMap<String, Integer>> czbkMap = new HashMap<>();

        // 创建基础班集合对象
        HashMap<String, Integer> jcMap = new HashMap<>();
        // 添加元素
        jcMap.put("陈玉楼", 20);
        jcMap.put("高跃", 22);
        // 把基础班添加到大集合
        czbkMap.put("jc", jcMap);

        // 创建就业班集合对象
        HashMap<String, Integer> jyMap = new HashMap<String, Integer>();
        // 添加元素
        jyMap.put("李杰", 21);
        jyMap.put("曹石磊", 23);
        // 把基础班添加到大集合
        czbkMap.put("jy", jyMap);

        // 遍历集合
        Set<String> czbkMapSet = czbkMap.keySet();
        for (String czbkMapKey : czbkMapSet) {
            System.out.println(czbkMapKey);
            HashMap<String, Integer> czbkMapValue = czbkMap.get(czbkMapKey);
            Set<String> czbkMapValueSet = czbkMapValue.keySet();
            for (String czbkMapValueKey : czbkMapValueSet) {
                Integer czbkMapValueValue = czbkMapValue.get(czbkMapValueKey);
                System.out.println("\t" + czbkMapValueKey + "---" + czbkMapValueValue);
            }
        }
    }
}

【HashMap嵌套ArrayList】

  假设HashMap集合的元素是ArrayList。有3个。每一个ArrayList集合的值是字符串。

  结果:

  三国演义
    吕布
    周瑜
  笑傲江湖
    令狐冲
    林平之
  神雕侠侣
    郭靖
    杨过

public class HashMapIncludeArrayListDemo {
    public static void main(String[] args) {
        // 创建集合对象
        HashMap<String, ArrayList<String>> map = new HashMap<>();

        // 创建集合元素1
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("吕布");
        list1.add("周瑜");
        map.put("三国演义", list1);

        // 创建元素集合2
        ArrayList<String> list2 = new ArrayList<>();
        list2.add("令狐冲");
        list2.add("林平之");
        map.put("笑傲江湖", list2);

        // 创建元素集合3
        ArrayList<String> list3 = new ArrayList<>();
        list3.add("郭靖");
        list3.add("杨过");
        map.put("神雕侠侣", list3);
        
        // 遍历集合
        Set<String> set = map.keySet();
        for (String key : set) {
            System.out.println(key);
            ArrayList<String> value = map.get(key);
            for (String s : value) {
                System.out.println("\t" + s);
            }
        }
    }
}

【ArrayList嵌套HashMap】

  需求:假设ArrayList集合的元素是HashMap。有3个。每一个HashMap集合的键和值都是字符串。元素我已经完成,请遍历。
  结果:
    周瑜---小乔
    吕布---貂蝉

    郭靖---黄蓉
    杨过---小龙女

    令狐冲---任盈盈
    林平之---岳灵珊

public class ArrayListIncludeHashMapDemo {
    public static void main(String[] args) {
        // 创建集合对象
        ArrayList<HashMap<String, String>> list = new ArrayList<>();

        // 创建元素1
        HashMap<String, String> map1 = new HashMap<>();
        map1.put("周瑜", "小乔");
        map1.put("吕布", "貂蝉");
        // 把元素添加到list里面
        list.add(map1);

        // 创建元素2
        HashMap<String, String> map2 = new HashMap<>();
        map2.put("郭靖", "黄蓉");
        map2.put("杨过", "小龙女");
        // 把元素添加到list里面
        list.add(map2);

        // 创建元素3
        HashMap<String, String> map3 = new HashMap<>();
        map3.put("令狐冲", "任盈盈");
        map3.put("林平之", "岳灵珊");
        // 把元素添加到list里面
        list.add(map3);
        
        // 遍历
        for (HashMap<String, String> map : list) {
            Set<String> set = map.keySet();
            for (String key : set) {
                String value = map.get(key);
                System.out.println(key + "---" + value);
            }
        }
    }
}

【HashMap嵌套HashMap嵌套HashMap】

  为了更符合要求:这次的数据就看成是学生对象。

  结果:

  传智播客
    bj   北京校区
        jc  基础班
            林青霞    27
            风清扬    30
        jy  就业班
            赵雅芝 28
            武鑫 29
    sh   上海校区
       jc   基础班
            郭美美 20
            犀利哥 22
       jy   就业班
            罗玉凤 21
            马征 23

public class HashMapDemo {
    public static void main(String[] args) {
        // 创建大集合
        HashMap<String, HashMap<String, ArrayList<Student>>> czbkMap = new HashMap<String, HashMap<String, ArrayList<Student>>>();

        // 北京校区数据
        HashMap<String, ArrayList<Student>> bjCzbkMap = new HashMap<String, ArrayList<Student>>();
        ArrayList<Student> array1 = new ArrayList<Student>();
        Student s1 = new Student("林青霞", 27);
        Student s2 = new Student("风清扬", 30);
        array1.add(s1);
        array1.add(s2);
        ArrayList<Student> array2 = new ArrayList<Student>();
        Student s3 = new Student("赵雅芝", 28);
        Student s4 = new Student("武鑫", 29);
        array2.add(s3);
        array2.add(s4);
        bjCzbkMap.put("基础班", array1);
        bjCzbkMap.put("就业班", array2);
        czbkMap.put("北京校区", bjCzbkMap);
// 上海校区数据
        HashMap<String, ArrayList<Student>> xaCzbkMap = new HashMap<String, ArrayList<Student>>();
        ArrayList<Student> array3 = new ArrayList<Student>();
        Student s5 = new Student("范冰冰", 27);
        Student s6 = new Student("刘意", 30);
        array3.add(s5);
        array3.add(s6);
        ArrayList<Student> array4 = new ArrayList<Student>();
        Student s7 = new Student("李冰冰", 28);
        Student s8 = new Student("张志豪", 29);
        array4.add(s7);
        array4.add(s8);
        xaCzbkMap.put("基础班", array3);
        xaCzbkMap.put("就业班", array4);
        czbkMap.put("上海校区", xaCzbkMap);

        // 遍历集合
        Set<String> czbkMapSet = czbkMap.keySet();
        for (String czbkMapKey : czbkMapSet) {
            System.out.println(czbkMapKey);
            HashMap<String, ArrayList<Student>> czbkMapValue = czbkMap
                    .get(czbkMapKey);
            Set<String> czbkMapValueSet = czbkMapValue.keySet();
            for (String czbkMapValueKey : czbkMapValueSet) {
                System.out.println("\t" + czbkMapValueKey);
                ArrayList<Student> czbkMapValueValue = czbkMapValue
                        .get(czbkMapValueKey);
                for (Student s : czbkMapValueValue) {
                    System.out.println("\t\t" + s.getName() + "---"
                            + s.getAge());
                }
            }
        }
    }
}

五、Map集合的小问题

【Hashtable和HashMap的区别】

  • Hashtable:线程安全,效率低。不允许null键和null值
  • HashMap:线程不安全,效率高。允许null键和null值
public class HashtableDemo {
    public static void main(String[] args) {
        // HashMap<String, String> hm = new HashMap<String, String>();
        Hashtable<String, String> hm = new Hashtable<String, String>();

        hm.put("it001", "hello");
        // hm.put(null, "world"); //NullPointerException
        // hm.put("java", null); // NullPointerException

        System.out.println(hm);
    }
}

【List,Set,Map等接口是否都继承自Map接口】

  List,Set不是继承自Map接口,它们继承自Collection接口
  Map接口本身就是一个顶层接口

posted @ 2019-05-15 22:48  yi0123  阅读(368)  评论(0编辑  收藏  举报