A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

day04  Map笔记    昨天我们学习了集合中的单列集合,今天我们来学习双列集合Map的相关知识。
    以下是今天的学习目标:
  • 能够说出Map集合特点
  • 使用Map集合添加方法保存数据
  • 使用”键找值”的方式遍历Map集合
  • 使用”键值对”的方式遍历Map集合
  • 能够使用HashMap存储自定义键值对的数据
  • 能够使用HashMap编写斗地主洗牌发牌案例


    以下是今天的详细笔记:


Map体系
Map集合概述
java.util.Map<K, V>接口

Map集合特点:
        1. 是双列集合, 一个元素包含两个值 (键key, 值value)
        2. key和value的类型可以相同, 也可以不同
        3. key不允许重复, value可以重复
        4. key和value是一一对应的, 一个键只能对应一个值

Map集合适合存储一对一关系的数据
Map常用子类
java.util.Map<K, V>接口: 双列集合的根接口, 规定了共性的方法
        |
        |_ HashMap<K, V>类: 底层哈希表. key存取无序不可重复
                |_ LinkedHashMap类: 底层哈希表+链表. key存取有序不可重复
               
映射: 键和值的对应关系 mapping

HashSet底层使用的就是HashMap
LinkedHashSet底层使用的就是LinkedHashMap
Map中常用方法
Map<String, Phone> map = new HashMap<>();
map.put("张三", p1);
map.put("张三", p2);

java.util.Map接口: 双列集合的顶层
        // 成员方法
        V put(K key, V value): 添加/修改键值对. 如果键存在, 则用新值替换已有值
        V remove(Object key): 根据键删除键值对, 返回被删除元素的值
        V get(Object key): 根据键获取值. 如果键不存在, 则返回null
        boolean containsKey(Object key): 判断是否包含指定的键
       
        Set<K> keySet(): 获取Map集合中所有的键, 存储到Set集合中
        Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的Entry对象的集合(Set集合)
补充:
entry 节点
Map key value

+---------------+
| K key, V value|
+---------------+

    public class Entry {
        K key;
        V value;
        K getKey();
        V getValue();
    }

5分钟练习: 测试Map常用方法
需求:
创建HashMap对象, 存储键值对都是String, 存储以下键值对
        黄晓明, 杨颖
        文章, 马伊琍
        邓超, 孙俪
删除文章马伊琍, 获取邓超的老婆打印出来, 判断键中是否有黄晓明
代码:
[Java] 纯文本查看 复制代码
/*
需求:
创建HashMap对象, 存储键值对都是String, 存储以下键值对
	黄晓明, 杨颖
	文章, 马伊琍
	邓超, 孙俪
删除文章马伊琍, 获取邓超的老婆打印出来, 判断键中是否有黄晓明
 */
public class Test {
    public static void main(String[] args) {
        // 创建HashMap对象
        Map<String, String> map = new HashMap<>();
        // 添加键值对
        map.put("黄晓明", "杨颖");
        map.put("文章", "马伊琍");
        map.put("邓超", "孙俪");
        System.out.println(map);

        // 删除文章马伊琍
        String v = map.remove("文章");
        System.out.println("删除文章后的返回值:" + v);
        System.out.println(map);

        // 获取邓超的老婆打印出来
        String v2 = map.get("邓超");
        System.out.println("邓超老婆:" + v2);

        // 判断键中是否有黄晓明
        boolean contains = map.containsKey("黄晓明");
        System.out.println("是否包含黄晓明:" + contains);
    }
}

Map遍历方式一: keySet()方法实现通过键找值
Set<K> keySet(): 获取Map集合中所有的键, 存储到Set集合中
       
keySet()遍历步骤:
        1. Map对象调用 keySet() 方法, 获取包含所有key的Set集合
        2. 遍历Set集合, 获取每个key
        3. 通过Map对象调用 get(Object key) 方法根据key获取到value
[Java] 纯文本查看 复制代码
Map<String, String> map = new HashMap<>();
	// keySet()遍历
	Set<String> keys = map.keySet();
	for (String key : keys) {
		// 通过每个键获取值
		String value = map.get(key);
		// 打印当前键值对
		System.out.println(key + "=" + value);
	}

5分钟练习: keySet()遍历Map
需求:
创建HashMap集合, 存储以下键值对
        "赵丽颖", 168
        "杨颖", 165
        "林志玲", 178
使用keySet()方式遍历Map, 打印键值对, 效果:
        林志玲=178
        赵丽颖=168
        杨颖=165
代码:
[Java] 纯文本查看 复制代码
/*
需求:
创建HashMap集合, 存储以下键值对
	"赵丽颖", 168
	"杨颖", 165
	"林志玲", 178
使用keySet()方式遍历Map, 打印键值对, 效果:
	林志玲=178
    赵丽颖=168
    杨颖=165
 */
public class Test {
    public static void main(String[] args) {
        // 创建Map对象
        HashMap<String, Integer> map = new HashMap<>();

        map.put("赵丽颖", 168);
        map.put("杨颖", 165);
        map.put("林志玲", 178);

        // 遍历方式1: keySet()
        Set<String> keys = map.keySet();
        for (String key : keys) {
            // 通过键获取值
            Integer value = map.get(key);
            // 打印
            System.out.println(key + "=" + value);
        }

        System.out.println("-------------");

        // 简化方式
        for (String key : map.keySet()) {
            // 通过键获取值
            Integer value = map.get(key);
            // 打印
            System.out.println(key + "=" + value);
        }
    }
}

Entry键值对对象介绍
java.util.Map.Entry接口:
        // 常用成员方法
        K getKey(): 获取Entry对象中的键
        V getValue(): 获取Entry对象中的值
       
Entry对象就是一个节点, 节点中存储了key和value
拿到一个Entry对象就可以从中获取key和value
[Java] 纯文本查看 复制代码
// HashMap中Entry实现类
	static class Node<K,V> implements Map.Entry<K,V> {
    	final int hash;
    	final K key;
    	V value;
    	Node<K,V> next;

    	public final K getKey() { 
        	return key; 
    	}
        
    	public final V getValue() { 
        	return value; 
    	}
	}

java.util.Map接口
        Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)

Map遍历方式2: 通过entrySet()获取Entry对象形式遍历
java.util.Map接口
        Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)

entrySet()方法遍历Map步骤:
        1. Map对象调用 entrySet() 获取包含所有Entry对象的Set集合
        2. 遍历Set集合, 获取每个Entry对象
        3. 调用Entry对象的 getKey() 和 getValue() 方法获取键和值
[Java] 纯文本查看 复制代码
Map<String, String> map = new HashMap<>();
	// keySet()遍历
	Set<Map.Entry<String, String>> entries = map.entrySet();
	for (Map.Entry<String, String> entry : entries) {
    	// 通过Entry对象获取每个键值对
    	String value = entry.getKey();
    	String value = entry.getValue();
    	// 打印当前键值对
    	System.out.println(key + "=" + value);
	}

5分钟练习: entrySet()遍历Map
需求:
创建HashMap集合, 存储以下键值对
        "赵丽颖", 168
        "杨颖", 165
        "林志玲", 178
使用entrySet()方式遍历Map, 打印键值对, 效果:
        赵丽颖=168
        杨颖=165
        林志玲=178
代码:
[Java] 纯文本查看 复制代码
/*
需求:
需求:
创建HashMap集合, 存储以下键值对
	"赵丽颖", 168
	"杨颖", 165
	"林志玲", 178
使用entrySet()方式遍历Map, 打印键值对, 效果:
	赵丽颖=168
	杨颖=165
	林志玲=178
 */
public class Test {
    public static void main(String[] args) {
        // 创建Map集合
        HashMap<String, Integer> map = new HashMap<>();

        // 添加键值对
        map.put("赵丽颖", 168);
        map.put("杨颖", 165);
        map.put("林志玲", 178);

        // 遍历方式2: 使用entrySet
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
        for (Map.Entry<String, Integer> entry : entrySet) {
            // 通过entry对象获取键和值
            String key = entry.getKey();
            Integer value = entry.getValue();
            // 打印
            System.out.println(key + "=" + value);
        }

        System.out.println("-----------");

        // 简化方式
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            // 通过entry对象获取键和值
            String key = entry.getKey();
            Integer value = entry.getValue();
            // 打印
            System.out.println(key + "=" + value);
        }
    }
}

HashMap存储自定义数据类型作为键
HashMap存储自定义JavaBean对象作为key保证key唯一不重复, 需要让JavaBean重写 hashCode() 和 equals() 方法
5分钟练习: 使用自定义JavaBean作为key需求:
定义Person类:
        私有成员变量: 姓名String name, 年龄int age
        生成无参/有参构造, set/get方法, toString, hashCode(), equals()
定义测试类:
        创建4个Person对象, 分别如下:
                "女王", 18
                "秦始皇", 18
                "普京", 30
                "女王", 18
        创建HashMap集合, 存入键为4个Person对象, 值为对应国家字符串: 英国, 秦国, 俄罗斯, 毛里求斯
        使用entrySet()方法遍历Map集合, 输出键值对
代码:
[Java] 纯文本查看 复制代码
public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(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 String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建4个Person对象
        Person p1 = new Person("女王", 18);
        Person p2 = new Person("秦始皇", 18);
        Person p3 = new Person("普京", 30);
        Person p4 = new Person("女王", 18);
        // 创建集合
        HashMap<Person, String> map = new HashMap<>();
        // 添加键值对
        map.put(p1, "英国");
        map.put(p2, "秦国");
        map.put(p3, "俄罗斯");
        map.put(p4, "毛里求斯");
        // 使用entrySet()方法遍历Map集合, 输出键值对
        for (Map.Entry<Person, String> entry : map.entrySet()) {
            // 获取键值对
            Person key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "-->" + value);
        }
    }
}

LinkedHashMap类
LinkedHashMap底层: 哈希表 + 链表
        key不允许重复, 但key存取有序
LinkedHashSet的底层就是使用LinkedHashMap实现的
Hashtable类
Hashtable和HashMap:
        相同点:
                1. 底层都是哈希表
        不同点:
                1. Hashtable不允许存储null值和null键; HashMap允许存储null值和null键
                2. Hashtable线程安全效率低; HashMap线程不安全效率高
               
Hashtable目前很少使用

Properties集合




Map集合练习5分钟练习: 利用Map计算字符串中每个字符出现的次数
需求: 键盘录入一个字符串, 计算一个字符串中每个字符出现次数

"aaabbbbccc"
a:3
b:4
c:3
key: 字符  value: 次数
Map<Charactor, Integer>
一对一关系, Map


char[] chs = "".toCharArray();
for () {
    char ch;
    boolean b = map.containsKey(key);
    if (b == true) {
            // map中有该字符, 已经存过
        Integer value = map.get(ch);
        value++;
        map.put(ch, value)
    } else {
        // map中没有存储该字符和次数的关系, 这是第一次发现该字符
        // 存入字符和次数
        map.put(ch, 1);
    }
}

分析:
    1.使用Scanner获取用户输入的字符串
    2.创建Map集合,key是字符串中的字符,value是字符的个数
    3.遍历字符串,获取每一个字符
    4.使用获取到的字符,去Map集合判断key是否存在
        key存在:
            通过字符(key),获取value(字符个数)
            value++
            put(key,value)把新的value存储到Map集合中
        key不存在:
                put(key,1)
    5.遍历Map集合,输出结果
代码:
[Java] 纯文本查看 复制代码
public class Test {
    public static void main(String[] args) {
        // 键盘录入
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入字符串:");
        String s = scanner.next();

        // 创建Map
        Map<Character, Integer> map = new HashMap<>();

        // 将字符串转换为char[], 并遍历
        for (char c : s.toCharArray()) {
            // 根据取到的字符c作为key, 判断是否已经存储过
            if (map.containsKey(c)) {
                // 存储过, 将次数取出加一再存回
                Integer value = map.get(c);
                value++;
                map.put(c, value);
            } else {
                // 没有存储过, 第一次遇到, 次数为1存储
                map.put(c, 1);
            }
        }

        // 遍历
        for (Character c : map.keySet()) {
            Integer value = map.get(c);
            System.out.println(c + "=" + value);
        }
    }
}

JDK9对集合添加的优化
使用集合添加大量元素时, 反复add(...)比较麻烦. JDK 9 为集合提供了一些静态方法, 可以方便的对集合进行初始化

java.util.List
        // 静态方法
        static List<E> of(E... e): 返回包含指定元素的 不可变List 集合
       
java.util.Set
        // 静态方法
        static Set<E> of(E... e): 返回包含指定元素的 不可变Set 集合
       
java.util.Map
        // 静态方法
        static Map<K, V> of(K k1, V v1, ...): 返回包含指定键值对的 不可变Map 集合
       
注意:
        1. of() 方法只适用于List接口, Set接口, Map接口, 不适用于接接口的实现类
        2. of() 方法的返回值是一个不能改变的集合, 集合不能再使用 add(), put() 方法添加元素, 会抛出异常
        3. Set接口和Map接口在调用 of() 方法的时候, 不能有重复的元素, 否则会抛出异常
将 不可变集合 的元素转移到常见集合实现类中:
[Java] 纯文本查看 复制代码
// List
    ArrayList<Integer> list = new ArrayList<>(List.of(1,2,3));

	// Set
    HashSet<Integer> set = new HashSet<>(Set.of(1,2,3));

	// Map
    HashMap<Integer, String> map = new HashMap<>(Map.of(1,"a",2,"b"));

5分钟练习: 使用of方法快速添加元素
需求:
使用List.of()方法, 添加1,2,3, 获取集合对象, 使用增强for遍历打印输出
使用Set.of()方法, 添加1,2,3, 获取集合对象, 使用增强for遍历打印输出
使用Map.of()方法, 添加"a":1, "b":2, "c":3, 获取集合对象, 使用entrySet()遍历打印输出
代码:
[Java] 纯文本查看 复制代码
/*
需求:
使用List.of()方法, 添加1,2,3, 获取集合对象, 使用增强for遍历打印输出
使用Set.of()方法, 添加1,2,3, 获取集合对象, 使用增强for遍历打印输出
使用Map.of()方法, 添加"a":1, "b":2, "c":3, 获取集合对象, 使用entrySet()遍历打印输出
 */
public class Test {
    public static void main(String[] args) {
//        List<Integer> list = List.of(1, 2, 3);
//        List<Integer> list = List.of(1, 2, 3);  // int[] arr = {1,2,3,4}
//        ArrayList<Integer> arrayList = new ArrayList(list);

        ArrayList<Integer> arrayList = new ArrayList(List.of(1, 2, 3));

        arrayList.add(10);
        arrayList.add(20);
        for (Integer integer : arrayList) {
            System.out.println(integer);
        }

//        Set<Integer> set = Set.of(1, 2, 3);
        HashSet<Integer> set = new HashSet<>(Set.of(1, 2, 3));
        set.add(10);
        for (Integer integer : set) {
            System.out.println(integer);
        }

//        Map<String, Integer> map = Map.of("a", 1, "b", 2, "c", 3);
        Map<String, Integer> map = new HashMap<>(Map.of("a", 1, "b", 2, "c", 3));
        map.put("z", 123);
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + "=" + value);
        }
    }
}

Debug调试模式介绍
断点:
        breakpoint, 在debug模式下, 程序运行到断点会暂停住, 便于我们在程序运行过程中查看
Debug调试程序:
    可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug
使用方式:
    在行号的右边,鼠标左键单击,添加断点(每个方法的第一行,哪里有bug添加到哪里)
    右键,选择Debug执行程序
    程序就会停留在添加的第一个断点处
执行程序:
    f8:逐行执行程序
    f7:进入到方法中
    shift+f8:跳出方法
    f9:跳到下一个断点,如果没有下一个断点,那么就结束程序
    ctrl+f2:退出debug模式,停止程序
    Console:切换到控制台
5分钟练习: 使用debug
需求:
使用以下代码练习debug调试
代码:
[Java] 纯文本查看 复制代码
public static void main(String[] args) {
    print(5);
}

public static void print(int n) {
    for (int i = 0; i < n; i++) {
        System.out.println("Hello world!");  // 加断点
    }
}

斗地主发牌案例有(大小)序斗地主: 需求分析
需求: 实现斗地主发牌, 特殊要求是看牌时按照从小到大顺序排列
分析:
        准备牌:
                为了牌有大小顺序, 给牌编号, 一个编号对应一张牌: 0=大王, 1=小王, 2=♤2, 3=♤3
                编号和牌是一对一映射关系, 用HashMap<Integer, String>存储, key为编号, value为牌
                再为编号单独创建一个额外的ArrayList<Integer> pokerIndex
                定义编号变量 int index = 0;
                按照编号添加大小王
                嵌套for循环拼接序号和花色, 存入HashMap, 同时将编号存入ArrayList, 编号index++
        洗牌:
                Collections.shuffle(List list)将编号的ArrayList pokerIndex打乱顺序
        发牌:
                创建4个ArrayList, 前3个是玩家, 最后一个是底牌
                遍历编号的ArrayList pokerIndex, 发编号给玩家, >=51时发给底牌
                对4个ArrayList排序
        看牌
                定义方法: public static void lookPoker(String name,HashMap<Integer,String> poker,ArrayList<Integer> list)
                        打印姓名, 遍历编号集合ArrayList<Integer> list获取每个编号, 通过编号从HashMap<Integer,String> poker中获取牌, 打印
                调用方法
斗地主案例之发牌看牌代码编写10分钟练习: 实现斗地主
代码:
[Java] 纯文本查看 复制代码
/*
有序斗地主
 */
public class Test {
    public static void main(String[] args) {
        /*
        准备牌
         */
        // 创建牌的Map集合
        HashMap<Integer, String> poker = new HashMap<>();
        // 创建牌的编号(索引)的集合
        ArrayList<Integer> pokerIndex = new ArrayList<>();
        // 创建存储花色的集合
        List<String> colors = List.of("♤", "♡", "♣", "♢");
        // 创建存储面值的集合
        List<String> numbers = List.of("2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
        // 定义牌的编号(索引)
        int index = 0;
        // 添加大小王
        poker.put(index, "大王");
        pokerIndex.add(index);
        index++;
        poker.put(index, "小王");
        pokerIndex.add(index);
        index++;
        // 添加其他牌
        for (String number : numbers) {
            for (String color : colors) {
                poker.put(index, color + number);
                pokerIndex.add(index);
                index++;
            }
        }
//        System.out.println(poker);
//        System.out.println(pokerIndex);

        /*
        洗牌: 实际上是洗牌的编号的集合
         */
        Collections.shuffle(pokerIndex);

        /*
        发牌
         */
        ArrayList<Integer> player01 = new ArrayList<>();
        ArrayList<Integer> player02 = new ArrayList<>();
        ArrayList<Integer> player03 = new ArrayList<>();
        ArrayList<Integer> dipai = new ArrayList<>();
        for (int i = 0; i < pokerIndex.size(); i++) {
            // 获取牌的编号
            int in = pokerIndex.get(i);
            // 判断存入哪里
            if (i >= 51) {
                // 存入底牌
                dipai.add(in);
            } else if (i % 3 == 0) {
                // 存入玩家1
                player01.add(in);
            } else if (i % 3 == 1) {
                // 存入玩家2
                player02.add(in);
            } else if (i % 3 == 2) {
                // 存入玩家3
                player03.add(in);
            }
        }

        /*
        排序
         */
        Collections.sort(player01);
        Collections.sort(player02);
        Collections.sort(player03);
        Collections.sort(dipai);

        /*
        看牌
         */
        lookPai("刘备", poker, player01);
        lookPai("关羽", poker, player02);
        lookPai("张飞", poker, player03);
        lookPai("底牌", poker, dipai);
    }

    // 看牌方法
    public static void lookPai(String name, HashMap<Integer, String> poker, ArrayList<Integer> player) {
        // 打印玩家名字
        System.out.print(name + ": ");
        // 遍历输出牌
        for (Integer in : player) {
            // 通过键获取值
            String pai = poker.get(in);
            // 打印牌
            System.out.print(pai + " ");
        }
        // 全都打印完毕后换行
        System.out.println();
    }
}

今日APIjava.util.Map接口: 双列集合的顶层
        // 成员方法
        V put(K key, V value): 添加/修改键值对. 如果键存在, 则用新值替换已有值
        V remove(Object key): 根据键删除键值对, 返回被删除元素的值
        V get(Object key): 根据键获取值. 如果键不存在, 则返回null
        boolean containsKey(Object key): 判断是否包含指定的键
        Set<K> keySet(): 获取Map集合中所有的键, 存储到Set集合中
        Set<Map.Entry<K,V>> entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)
       
// JDK 9对于集合初始化的优化
java.util.List
        // 静态方法
        static List<E> of(E... e): 返回包含指定元素的 不可变List 集合
       
java.util.Set
        // 静态方法
        static Set<E> of(E... e): 返回包含指定元素的 不可变Set 集合
       
java.util.Map
        // 静态方法
        static Map<K, V> of(K k1, V v1): 返回包含指定键值对的 不可变Map 集合

Map接口: 双列集合的根接口, 规定了共性的方法
        |_ HashMap类: 底层哈希表=数组+链表+红黑树. key无序不可重复, 可存null键null值, 线程不安全效率高
        |        |_ LinkedHashMap类: 哈希表+双向链表. 哈希表实现key不可重复, 双向链表实现key存取有序
        |
        |_ Hashtable类: 底层哈希表. Hash特性针对key, key无序不可重复, 不可存null键null值, 线程安全效率低
        |_ TreeMap类: 底层红黑树结构(存入时就排序). Tree特性针对key, 可以按照key排序, 要求key具备比较性
        |_ 遍历
                |_ keySet(): 获取所有key组成的Set集合, 遍历Set集合拿到key, 通过Map的get(key)得到value
                |        |_ 对于Set<Key>的遍历
                |                |_ 增强for
                |                |_ 迭代器
                |_ entrySet(): 获取所有的key和value组成的Entry对象的Set集合, 遍历出entry对象, 通过entry对象的getKey()获取对应的key, 通过Entry对象的getValue方法获取对应的value
                        |_ 对Set<Entry>的遍历
                                |_ toArray()
                                |_ 增强for
                                |_ 迭代器





















1 个回复

倒序浏览
消灭;零回复!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马