【Map】

本文围绕Java的Map集合展开,介绍了Map接口特点、常用子类(如HashMap、LinkedHashMap),讲解了数据存取方法及两种遍历方式。还提到使用HashMap存储自定义类型键值的要点,补充了JDK9集合添加优化和Debug追踪知识,最后给出模拟斗地主洗牌发牌的案例及实现步骤。

【Map】

主要内容

  • Map集合

教学目标

  • 能够说出Map集合特点

    • 存储的元素一对,键不能重复,通过键获取值
  • 使用Map集合添加方法保存数据

    • put(K,V)
  • 使用”键找值”的方式遍历Map集合

    • 1: 获取所有的键 .keySet()
    • 2:遍历键,获取值 get(k)
  • 使用”键值对”的方式遍历Map集合

    • Entry是Map的内部接口 ,一个Entry对象就是一个结婚证
      • getKey() 键 getValue()值
    • map.entrySet() :获取所有的键值对对象,放到set集合
    • 遍历set集合,entry对象获取键和值
  • 能够使用HashMap存储自定义键值对的数据

    • 要重写equals和hashCode 和HashSet一样的道理
  • 能够使用HashMap编写斗地主洗牌发牌案例

第一章 Map集合

1.1 Map接口介绍

学习目标

  • 能够说出Map集合特点
    • 双列集合(元素是一对一对儿的),键的值不能重复

内容详解

现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map<K,V>接口。

java.util.Map<K,V>接口是最高接口,注意和Collection没有直接联系。
第一个泛型代表键是什么类型。
第二个泛型代表值是什么类型。
二者可以同类型,也可以不同类型。

我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同,如下图。

在这里插入图片描述

  1. Collection中的集合,元素是孤立存在的,Map中的集合,元素是成对存在的,每个元素由键与值两部分组成。因此Collection中的集合称为单列集合Map中的集合称为双列集合,这两个接口没有任何关系
  2. Map集合使用时是通过键去获取值,每个键只能对应一个值。需要注意的是,Map集合不能包含重复的键,但值可以重复。

1.2 Map常用子类

学习目标

  • 认识Map中常用的子类知道其各自特点
    • HashMap<K,V>: 快,键唯一,自定义键重写hashCode和equals 不能保证才存取顺序
    • LinkedHashMap<K,V>: 继承了HashMap的特点,但是有存取顺序

内容详解

通过查看Map接口描述,看到Map有多个子类,这里我们主要讲解常用的HashMap集合、LinkedHashMap集合。

  1. HashMap<K,V>:存储数据采用的哈希表结构,元素的存取顺序不能保证一致。
    • 由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
  2. LinkedHashMap<K,V>:HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
由于Map当中的key不能重复,所以如果使用自定义类型作为Map当中的key,必须覆盖重写equals和hashCode方法。

tips:Map接口中的集合都有两个泛型变量<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量<K,V>的数据类型可以相同,也可以不同。

1.3 Map中数据存取常用方法

学习目标

  • 熟练掌握Map集合的常用数据存取的方法

内容详解

Map接口中定义了很多方法,常用的如下:

public  V put(K key, V value):添加一个键值对儿。返回被替换的本来value值。
public  V remove(Object obj):根据键,删除对应的键值对儿。返回被删掉的value值。
public  V get(Object obj):根据键,获取对应的value值。
default V getOrDefault (Object key, V defaultValue) :返回指定键映射到的值,如果此映射不包含该键的映射,则返回 defaultValue。
public Set<K> keySet():获取所有的键的集合。(值不重复)
public Collection<V> values():获取所有的值的集合。(值可能重复)

Map接口的方法演示

public class Demo01Map {

    public static void main(String[] args) {
        // 左边的String代表键key是字符串;右边的String代表值value也是字符串
        // 第一个泛型代表学号,第二个泛型代表的是姓名
        Map<String, String> map = new HashMap<>();
			  // put 
        map.put("IT001", "王宝强");
        map.put("IT002", "贾乃亮");
        String result = map.put("IT003", "陈羽凡");
        System.out.println(result); // null
        System.out.println(map); // {IT003=陈羽凡, IT002=贾乃亮, IT001=王宝强}

        result = map.put("IT003", "马伊琍");
        System.out.println("被替换掉的是:" + result); // 陈羽凡
        System.out.println(map); // {IT003=马伊琍, IT002=贾乃亮, IT001=王宝强}
        System.out.println("===================");

        // remove
        result = map.remove("IT002");
        System.out.println("被删掉的人是:" + result); // 贾乃亮
        System.out.println(map); // {IT003=马伊琍, IT001=王宝强}

        result = map.remove(100);
        System.out.println(result); // null,因为key是不存在的。
        System.out.println("===================");

        // get
        result = map.get("IT001");
        System.out.println(result); // 王宝强

        result = map.get("IT007");
        System.out.println(result); // null
        System.out.println("===================");
      	
        result = map.getOrDefault("IT008", "没有这个人");
        System.out.println(result); // 没有这个人
        System.out.println("===================");

        // keySet
        Set<String> keys = map.keySet(); // 获取了其中所有的键
        for (String key : keys) {
            System.out.println(key);
        }
        System.out.println("===================");

        // values
        Collection<String> values = map.values(); // 获取了其中所有的值
        Iterator<String> iter = values.iterator();
        while (iter.hasNext()) {
            System.out.println(iter.next());
        }
    }

}

tips:

使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中;

若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。

1.4 KeySet方式遍历Map集合

既然Map和Collection不一样,那么Map也没有继承Iterable接口,所以Map集合不能直接使用迭代器。我们可以借助keySet方法获取所有键值,通过遍历键查找对应的值。

学习目标

  • 能够使用keySet方法遍历Map集合

步骤分析

  1. 获取Map中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。方法提示:keyset()
  2. 遍历键的Set集合,得到每一个键。
  3. 根据键,获取键所对应的值。方法提示:get(K key)

代码实现

public class Demo02MapKeySet {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("王宝强", "马蓉");
        map.put("谢霆锋", "张柏芝");
        map.put("陈羽凡", "白百何");
        map.put("武大郎", "潘金莲");

        Set<String> keys = map.keySet(); // 获取所有的键
        for (String key : keys) { // 通过键查找值
            String value = map.get(key);
            System.out.println(key + "的老婆是" + value);
        }
     }
}

知识小结

思路:获取所有的键,遍历键获取对应的值

1.5 键值对对象方式遍历Map集合

学习目标

  • 掌握如何获取键值对对象
  • 掌握借助键值对对象遍历Map集合的操作

内容详解

1.5.1 认识Entry键值对对象

我们已经知道,Map中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在Map中是一一对应关系,这一对对象又称做Map中的一个Entry(项)Entry将键值对的对应关系封装成了对象,即键值对对象, 这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。

[外链图片转存失败(img-rWz2YoiX-1562480276620)(img/02-Entry键值对的概念.png)]

既然Entry表示了一对键和值,那么也同样提供了获取对应键和对应值得方法:

  1. public K getKey():获取Entry对象中的键。
  2. public V getValue():获取Entry对象中的值。

在Map集合中也提供了获取所有Entry对象的方法:

  1. public Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)。
1.5.2 键值对方式遍历Map集合
步骤分析
  1. 获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。方法提示:entrySet()
  2. 遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。
  3. 通过键值对(Entry)对象,获取Entry对象中的键与值。 方法提示:getkey() getValue()
代码实现
public class Demo03MapEntrySet {

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("王宝强", "马蓉");
        map.put("谢霆锋", "张柏芝");
        map.put("陈羽凡", "白百何");
        map.put("武大郎", "潘金莲");

        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + " - " + value);
        }
    }
}

tips:Map集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了。

1.6 HashMap存储自定义类型键值

学习目标

  • 能够使用HashMap存储自定义类型的键值

内容详解

Map集合的键是唯一的,如果我们需要使用自定义类型当做键值,那么这个自定义类型一定需要进行重写hashCode()和equals()方法。道理和前一天的HashSet是一样的。

练习巩固

练习

每位学生(姓名,年龄)都有自己的家庭住址。那么,既然有对应关系,则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。

注意,学生姓名相同并且年龄相同视为同一名学生。

步骤分析
  1. 先定义学生类,重写hashCode方法和equals方法
  2. 创建若干学生对象
  3. 创建Map<Student,String>对象
  4. 将学生对象和地址借助put方法存储到集合中
  5. 遍历集合查看结果
代码实现

编写学生类

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 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 int hashCode() {
        return Objects.hash(name, age);
    }
}

编写测试类:

public class HashMapTest {
    public static void main(String[] args) {
        //1,创建Hashmap集合对象。
        Map<Student,String>map = new HashMap<Student,String>();
        //2,添加元素。
        map.put(newStudent("lisi",28), "上海");
        map.put(newStudent("wangwu",22), "北京");
        map.put(newStudent("zhaoliu",24), "成都");
        map.put(newStudent("zhouqi",25), "广州");
        map.put(newStudent("wangwu",22), "南京");
        
        //3,取出元素。键找值方式
        Set<Student>keySet = map.keySet();
        for(Student key: keySet){
            Stringvalue = map.get(key);
            System.out.println(key.toString()+"....."+value);
        }
    }
}

知识小结

  1. 当给HashMap中存放自定义对象时,如果自定义对象作为key存在,这时要保证对象唯一,必须复写对象的hashCode和equals方法,原理和HashSet存放自定义对象一致。

1.8 LinkedHashMap

我们知道HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢?可以使用java.util.LinkedHashMap集合来存放。

学习目标

  • 掌握LinkedHashMap的特性

内容详解

在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。

public class LinkedHashMapDemo {
    public static void main(String[] args) {
        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
        map.put("邓超", "孙俪");
        map.put("李晨", "范冰冰");
        map.put("刘德华", "朱丽倩");
        Set<Entry<String, String>> entrySet = map.entrySet();
        for (Entry<String, String> entry : entrySet) {
            System.out.println(entry.getKey() + "  " + entry.getValue());
        }
    }
}

结果:

邓超  孙俪
李晨  范冰冰
刘德华  朱丽倩

1.9 Map集合练习

题目需求

计算一个字符串中每个字符出现次数。

例如输入:abcabcabaaa
结果是:
a - 6
b - 3
c - 2

步骤分析

  1. 获取一个字符串对象
  2. 创建一个Map集合,键代表字符,值代表次数。
  3. 遍历字符串得到每个字符。
  4. 判断Map中是否有该键。
  5. 如果没有,第一次出现,存储次数为1;如果有,则说明已经出现过,获取到对应的值进行++,再次存储。
  6. 打印最终结果

代码实现

public class Demo01MapCount {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串:");
        String str = sc.next();
      
        // key代表字符,value代表出现的次数
        Map<Character, Integer> map = new HashMap<>();

        for (int i = 0; i < str.length(); i++) {
            char ch = str.charAt(i); // 当前单个字符
            //方式一
            int num = map.getOrDefault(ch, 0);
            map.put(ch, ++num);
						//方式二
//            if (map.containsKey(ch)) {
//                int num = map.get(ch);
//                num++;
//                map.put(ch, num);
//            } else {
//                map.put(ch, 1);
//            }
        }
        System.out.println(map);
    }
}

课后作业:

Map<"网址",次数> , 统计出前次数排名前5的网址。

map.put("www.jd.com",100);

map.put("www.jd.com",100);

map.put("www.jd.com",100);

第二章 补充知识点

2.1 JDK9对集合添加的优化(of)

通常,我们在代码中创建一个集合(例如,List 或 Set ),并直接用一些元素填充它。 实例化集合,几个 add方法 调用,使得代码重复。

public class Demo01 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("abc");
        list.add("def");
        list.add("ghi");
        System.out.println(list);
    }
}

Java 9,添加了几种集合工厂方法,更方便创建少量元素的集合、map实例。新的List、Set、Map的静态工厂方法可以更方便地创建集合的不可变实例。

例子:

public class HelloJDK9 {  
    public static void main(String[] args) {  
        Set<String> str1=Set.of("a","b","c");  
        //str1.add("c");这里编译的时候不会错,但是执行的时候会报错,因为是不可变的集合  
        System.out.println(str1);  
        Map<String,Integer> str2=Map.of("a",1,"b",2);  
        System.out.println(str2);  
        List<String> str3=List.of("a","b");  
        System.out.println(str3);  
    }  
} 

需要注意以下两点:

1:of()方法只是Map,List,Set这三个接口的静态方法,其父类接口和子类实现并没有这类方法,比如 HashSet,ArrayList等待;

2:返回的集合是不可变的,不能增删数据。

2.2 Debug追踪

使用IDEA的断点调试功能,查看程序的运行过程

  1. 在有效代码行,点击行号右边的空白区域,设置断点,程序执行到断点将停止,我们可以手动来运行程序

    在这里插入图片描述

  2. 点击Debug运行模式 在这里插入图片描述

  3. 程序停止在断点上不再执行,而IDEA最下方打开了Debug调试窗口

    在这里插入图片描述
    在这里插入图片描述

  4. Debug调试窗口介绍

    在这里插入图片描述

  5. 快捷键F8,代码向下执行一行,第九行执行完毕,执行到第10行(第10行还未执行)

在这里插入图片描述

  1. 切换到控制台面板,控制台显示 请录入一个字符串: 并且等待键盘录入

    在这里插入图片描述

  2. 快捷键F8,程序继续向后执行,执行键盘录入操作,在控制台录入数据 ababcea
    在这里插入图片描述

    回车之后效果:在这里插入图片描述

    调试界面效果:在这里插入图片描述

  3. 此时到达findChar方法,快捷键F7,进入方法findChar

在这里插入图片描述

  1. 快捷键F8 接续执行,创建了map对象,变量区域显示
    在这里插入图片描述

  2. 快捷键F8 接续执行,进入到循环中,循环变量i为 0,F8再继续执行,就获取到变量c赋值为字符‘a’ 字节值97
    在这里插入图片描述

  3. 快捷键F8 接续执行,进入到判断语句中,因为该字符 不在Map集合键集中,再按F8执行,进入该判断中

在这里插入图片描述

  1. 快捷键F8 接续执行,循环结束,进入下次循环,此时map中已经添加一对儿元素

    在这里插入图片描述

  2. 快捷键F8 接续执行,进入下次循环,再继续上面的操作,我们就可以看到代码每次是如何执行的了
    在这里插入图片描述

  3. 如果不想继续debug,那么可以使用快捷键F9,程序正常执行到结束,程序结果在控制台显示
    在这里插入图片描述

第三章 模拟斗地主洗牌发牌

3.1 案例介绍

按照斗地主的规则,完成洗牌发牌的动作。

在这里插入图片描述

具体规则:

  1. 组装54张扑克牌将
  2. 54张牌顺序打乱
  3. 三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
  4. 查看三人各自手中的牌(按照牌的大小排序)、底牌

规则:手中扑克牌从大到小的摆放顺序:大王,小王,2,A,K,Q,J,10,9,8,7,6,5,4,3

3.2 案例需求分析

  1. 准备牌:

完成数字与纸牌的映射关系:

使用双列Map(HashMap)集合,完成一个数字与字符串纸牌的对应关系(相当于一个字典)。

  1. 洗牌:

通过数字完成洗牌发牌

  1. 发牌:

将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。

存放的过程中要求数字大小与斗地主规则的大小对应。

将代表不同纸牌的数字分配给不同的玩家与底牌。

  1. 看牌:

通过Map集合找到对应字符展示。

通过查询纸牌与数字的对应关系,由数字转成纸牌字符串再进行展示。

在这里插入图片描述

3.3 实现代码步骤

public class Poker {
    public static void main(String[] args) {
        /*
         * 1组装54张扑克牌
         */
        // 1.1 创建Map集合存储
        HashMap<Integer, String> pokerMap = new HashMap<Integer, String>();
        // 1.2 创建 花色集合 与 数字集合
        ArrayList<String> colors = new ArrayList<String>();
        ArrayList<String> numbers = new ArrayList<String>();

        // 1.3 存储 花色 与数字
        Collections.addAll(colors, "♦", "♣", "♥", "♠");
        Collections.addAll(numbers, "2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
        // 设置 存储编号变量
        int count = 1;
        pokerMap.put(count++, "大王");
        pokerMap.put(count++, "小王");
        // 1.4 创建牌 存储到map集合中
        for (String number : numbers) {
            for (String color : colors) {
                String card = color + number;
                pokerMap.put(count++, card);
            }
        }
        /*
         * 2 将54张牌顺序打乱
         */
        // 取出编号 集合
        Set<Integer> numberSet = pokerMap.keySet();
        // 因为要将编号打乱顺序 所以 应该先进行转换到 list集合中
        ArrayList<Integer> numberList = new ArrayList<Integer>();
        numberList.addAll(numberSet);

        // 打乱顺序
        Collections.shuffle(numberList);

        // 3 完成三个玩家交替摸牌,每人17张牌,最后三张留作底牌
        // 3.1 发牌的编号
        // 创建三个玩家编号集合 和一个 底牌编号集合
        ArrayList<Integer> noP1 = new ArrayList<Integer>();
        ArrayList<Integer> noP2 = new ArrayList<Integer>();
        ArrayList<Integer> noP3 = new ArrayList<Integer>();
        ArrayList<Integer> dipaiNo = new ArrayList<Integer>();

        // 3.2发牌的编号
        for (int i = 0; i < numberList.size(); i++) {
            // 获取该编号
            Integer no = numberList.get(i);
            // 发牌
            // 留出底牌
            if (i >= 51) {
                dipaiNo.add(no);
            } else {
                if (i % 3 == 0) {
                    noP1.add(no);
                } else if (i % 3 == 1) {
                    noP2.add(no);
                } else {
                    noP3.add(no);
                }
            }
        }

        // 4 查看三人各自手中的牌(按照牌的大小排序)、底牌
        // 4.1 对手中编号进行排序
        Collections.sort(noP1);
        Collections.sort(noP2);
        Collections.sort(noP3);
        Collections.sort(dipaiNo);

        // 4.2 进行牌面的转换
        // 创建三个玩家牌面集合 以及底牌牌面集合
        ArrayList<String> player1 = new ArrayList<String>();
        ArrayList<String> player2 = new ArrayList<String>();
        ArrayList<String> player3 = new ArrayList<String>();
        ArrayList<String> dipai = new ArrayList<String>();

        // 4.3转换
        for (Integer i : noP1) {
            // 4.4 根据编号找到 牌面 pokerMap
            String card = pokerMap.get(i);
            // 添加到对应的 牌面集合中
            player1.add(card);
        }

        for (Integer i : noP2) {
            String card = pokerMap.get(i);
            player2.add(card);
        }
        for (Integer i : noP3) {
            String card = pokerMap.get(i);
            player3.add(card);
        }
        for (Integer i : dipaiNo) {
            String card = pokerMap.get(i);
            dipai.add(card);
        }

        //4.5 查看
        System.out.println("令狐冲:"+player1);
        System.out.println("石破天:"+player2);
        System.out.println("鸠摩智:"+player3);
        System.out.println("底牌:"+dipai);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值