结合题目具体讲解Map

目录

前言:

一、基本题

二、方法题

三、综合题


前言:

本篇文章将会结合三道题目,由浅入深,逐步讨论Map的实际应用。

感谢大家的观看。

首先,我们来回忆一下Map的基本方法。

size();

put(K key,V value);----给指定的key赋value值

putAll();

containsKey();----判断是否包含key

containsValue();---判断是否包含value

keySet();----返回集合中所有的key值

这篇文章的目的就是熟练掌握并灵活使用这些方法,以及更多的一些Map的。

OK,让我们开始本篇的第一道题。

一、基本题

------本题的目的在于了解map的作用,以及基本的遍历方式。

创建⼀个Map集合,里面有如下元素
{香蕉 = 5.6 ,樱桃 = 25 ,桃子 = 5.6 , 苹果 = 2.3}
按要求完成:
(1)遍历该map集合,并且统计有多少种水果(key),用两种方式进行遍历
(2)将"香蕉"的价格修改为10.9,并删除桃子这组数据,
(3)将修改后的map中所有key和value都输出到控制台

思考:

**--**水果和价格之间有什么关系?

**--**我们应该如何统计水果?统计的水果数的key的数量有什么关系?

**--**进阶:我们能否在遍历的同时修改价格和删除数据?

代码演示:

java 复制代码
public class Test {
    public static void main(String[] args) {
        Map<String,Double> tree=new TreeMap<>();
        tree.put("香蕉", 5.6);
        tree.put("樱桃", 25.0);
        tree.put("桃子", 5.6);
        tree.put("苹果", 2.3);
        //(1)遍历该map集合,并且统计有多少种⽔果(key),⽤两种⽅式进⾏遍历 
        //key值唯一,所以统计水果数量只需要加一个计数器。
        int num1=0;
        //遍历一:
        Set<String> set=tree.keySet();
        for(String str:set){
            System.out.print(str+"="+tree.get(str)+"  ");
            num1++;
        }
        System.out.println();
        System.out.println("水果数量:"+num1);
        //遍历二:
        //key值唯一,所以统计水果数量等同于长度
        Set<Map.Entry<String,Double>> entries=tree.entrySet();
         for(java.util.Map.Entry<String, Double> entry:entries){
            System.out.print(entry.getKey()+"="+entry.getValue()+"  ");
            num1++;
        }
        System.out.println();
        System.out.println("水果数量:"+tree.size());

        //(2)将"⾹蕉"的价格修改为10.9,并删除桃⼦这组数据,
        //修改,用put更新
        tree.put("香蕉", 10.9);
        //删除
        tree.remove("桃子");

        //(3)将修改后的map中所有key和value都输出到控制台
        //遍历
        Set<String> set1=tree.keySet();
        for(String str:set1){
            System.out.print(str+"="+tree.get(str)+"  ");
        }
    }
}

讲解:

**--**水果和价格之间有什么关系?

--(水果--价格)=(key--value),水果唯一,不同的水果可以拥有相同的价格。

**--**我们应该如何统计水果?统计的水果数的key的数量有什么关系?

--因为key值是唯一的,所以map的长度就等于水果种类数。

遍历一:

java 复制代码
    //遍历一:
    //将map的key键取出来存储入单列集合set
    Set<String> set=tree.keySet();
    //运用遍历单列集合的方法
    for(String str:set){
    //输出key--str 并运用key键在map中找出对应的value值
        System.out.print(str+"="+tree.get(str)+"  ");          
      }

遍历二:

java 复制代码
    //遍历二:
    //tree.entreSet()方法是将map中的键值封装为Map.Entry对象
    //Map.Entry对象:每个对象包含一对(key--value)
    //Set<Map.Entry<String,Double>> entries 将Map.Entry对象存储在单列集合中
    Set<Map.Entry<String,Double>> entries=tree.entrySet();
    //运用遍历单列集合的方法开始遍历
    for(java.util.Map.Entry<String, Double> entry:entries){
    //取出Map.Entry对象中的key-->entry.getKey()和value-->entry.getValue()
       System.out.print(entry.getKey()+"="+entry.getValue()+"  ");
    }

总结:

多列集合的遍历简单来说就是将其拆分成单列集合进行遍历。

--第一种方法运用了key唯一性和对应性;

--第二中方法相当于一个类,直接将key和value装在一起,再给Set。

二、方法题

---本题的目的在于熟练的运用方法,结合方法来解决问题。

统计以下字符串出现的次数:

--String s="aabbddccaefdd";

思考:

--我们如何存储这个字符串?字符和次数之间有什么关系?

--字符串的字符并不完全连续,我们应该如何计数?

代码演示:

java 复制代码
public class Test {
    public static void main(String[] args) {
        String s="aabbddccaefdd";
        //注意:如果是输入的字符需要判断是否为空的情况

        //摘出首字符
        char ch=s.charAt(0);
        //因为首字符必定有一个,所以我们设起始值为1
        int num=1;
        //(字符--数量)-->(key--value)-->创建Map来存储
        Map<Character,Integer> tree=new TreeMap<>();
        //循环开始
        for(int i=1;i<s.length();i++){
            //相同字符加一
            if(ch==s.charAt(i)){
                num++;
            }
            else {
                //字符更改,存储更改前字符及其数量
                //字符数量需要加上它过去所出现的数量
                int num2=(tree.get(ch)==null)?0:tree.get(ch);//字符过去数量
                tree.put(ch, num+num2);//添加入map
                ch=s.charAt(i);//更新字符
                num=1;//更新新字符数量
            }
        }
        //注意:循环无法存储最后字符所以在结束后,将末尾字符存入map
        int num2=(tree.get(ch)==null)?0:tree.get(ch);
        tree.put(ch, num+num2);
        //循环输出
        Set<Map.Entry<Character,Integer>> entries=tree.entrySet();
        for(java.util.Map.Entry<Character, Integer> entry:entries){
            System.out.print(entry.getKey()+":"+entry.getValue()+"   ");
        }
    }
}

讲解:

--我们如何存储这个字符串?字符和次数之间有什么关系?

--字符唯一,数量可以相同,符合(key--value),用map存储

--字符串的字符并不完全连续,我们应该如何计数?

--字符是key,可以根据key找出对于的数量,及过去数量;

--再用put更新字符数量,需要注意字符是否第一次存入map的情况。

总结:

知识并不算难,难的是把所有的知识总结应用,互相嵌套下,我们的思维需要极大的开阔。所以根基一定要打牢。

三、综合题

---map对用户的具体综合应用。

(1)键盘录入整行字符串,遇到quit结束录入,录⼊字符串格式为:学号.姓名.年龄.分数
(2)拆解上述整行的字符串得到属性值,然后实例化学生对象,按照"."分割字符串
(3)将学生对象添加到Map<学生,学号>集合中,要求:添加时 按照成绩逆序排列
(4)遍历输出集合
(5)判断 003号学生是否存在,如果存在,删除该学生
(6)再次遍历集合(使用另外一种方式)

【输入示例】:

001.南霄.18.95

003.北霄.81.59

quit

思考:

--我们如何进行删除操作?用map.remove吗?

进阶:有条件的可以完善代码,做出一个比较完整的程序。

代码演示:

测试类

java 复制代码
public class Test {
    public static void main(String[] args) {
        //连接键盘
        Scanner scanner=new Scanner(System.in);
        //创建map存储
        Map<Student,String> map=new TreeMap<>();
        //添加
        //循环添加 quit-->break退出
        while (true) {
        System.out.print("程序开始运行,请输入:");
        //String对象接受数据
        String string=scanner.nextLine();
        //退出程序
        if(string.equals("quit")){System.out.println("退出成功"); break;}
        //分割数据
        String[] str=string.split("\\.");
        //数据传入Student条件
            //数据数量条件
        if(str.length==4){
            //数据类型条件
          String id=str[0];
          String name=str[1];
          int age=Integer.parseInt(str[2]);
          int score=Integer.parseInt(str[3]);
            //传入Student
          Student student=new Student(id,name,age,score);
            //传入map
          map.put(student, id);
            //提醒用户
          System.out.println("添加成功");

          }
        }
            //遍历输出
          Set<Map.Entry<Student,String>> entries=map.entrySet();
          for(java.util.Map.Entry<Student, String> entry:entries){
          System.out.println(entry.getKey()+"---"+entry.getValue());}
          System.out.println("一次遍历成功");
            //删除
            //迭代器安全删除
            Iterator<Student> iterator=map.keySet().iterator();
            while(iterator.hasNext()){
                Student s=iterator.next();
                if(s.getId().equals("003")){
                    iterator.remove();
                    System.out.println("猜猜我偷偷删了什么!");
                }
            }
            //遍历输出
            Set<Student> set=map.keySet();
            for(Student student:set){
                System.out.println(student+"---"+map.get(student));
            }
            System.out.println("二次遍历成功");
        }
    }

基础类:

java 复制代码
public class Student implements Comparable<Student>{
    private String id;
    private String name;
    private int score;
    private int age;

//构造器...    
//.....get、set



//重写输出
    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", score=" + score + "]";
    }

//重写自然排序
    @Override
    public int compareTo(Student o) {
        int num;
        num=o.score-this.score;
        if(num==0){
            num=this.name.compareTo(o.name);
            if(num==0){
                num=this.age-o.age;
                if(num==0){
                    num=this.id.compareTo(o.id);
                }
            }
        }
        return num;
    }
}

讲解:

--我们如何进行删除操作?用map.remove吗?

--map.remove()?

--map.remove(key),可以通过key来进行数据的删除;

--但在增强for循环中进行map.remove,会捕获ConcurrentModificationException 异常;

--异常导致程序终止运行(不会报错);

--迭代器(Iterator)?

--迭代器中的remove()方法是专门为了在遍历中删除元素而存在的。

问题:为什么增强for循环中进行map.remove进行删除会捕获异常?

首先我们需要知道增强for循环的内部原理:

--增强for循环的底层为迭代器;

--很多集合内部维护着一个修改计数器;

--迭代器遍历访问时,会检查修改计数器,以保证元素安全。

所以:

当在增强for循环中使用map.remove,修改计数器会改变,迭代器检查到"意外修改",抛出异常,停止遍历

迭代器删除:

java 复制代码
    //迭代器安全删除
    //获取map中key集合的迭代器
    Iterator<Student> iterator=map.keySet().iterator();
    //遍历所有key
    //判断
    while(iterator.hasNext()){  //iterator.hasNext()当还有元素时,返回true
    //获取下一元素
        Student s=iterator.next();
    //删除条件
        if(s.getId().equals("003")){
        //调用迭代器remove,进行删除
           iterator.remove();
           System.out.println("猜猜我偷偷删了什么!");
         }
      }

我们显式调用迭代器时,可以直接使用iterator.remove();

该方法可以在删除元素的的同时更新迭代器内部状态,从而避免异常。

区别:

--增强for循环,隐式迭代器,无法调用iterator.remove();

--显示调用迭代器,可以调用iterator.remove()。

remove对比:

操作方式 是否修改计数器 迭代器是否感知修改 是否抛出异常
map.remove 否(迭代器不知情) 会抛出异常
iterator.remove() 是(同步更新记录) 不会抛出异常

循环对比:

循环方式 能否安全删除元素 适用场景
增强 for 循环 不能 仅遍历,不修改集合
普通 for 循环(遍历副本) 允许额外内存开销,简单删除
迭代器显式遍历 能(最优) 遍历中删除元素,无额外开销

希望本篇文章的三道题可以帮到大家!

后续,我们将进行斗地主发牌程序的讲解。

OK,本篇文章便到此结束!

夏日的暖阳炽热而温和;

似波,似水,似千秋万浪!

相关推荐
Joker-011114 天前
深入 Go 底层原理(十二):map 的实现与哈希冲突
算法·go·哈希算法·map
CAU界编程小白23 天前
C++STL系列之set和map系列
c++·stl·set·map
云边有个稻草人25 天前
【C++】第十八节—一文万字详解 | map和set的使用
c++·set·map·multimap·multiset·序列式容器和关联式容器
real_metrix2 个月前
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
c++·迭代器·迭代器失效·erase
SunkingYang2 个月前
C++中如何遍历map?
c++·stl·map·遍历·方法
编程版小新3 个月前
封装红黑树实现mymap和myset
c++·学习·set·map·红黑树·红黑树封装set和map·红黑树封装
熬夜学编程的小王3 个月前
【C++进阶篇】C++容器完全指南:掌握set和map的使用,提升编码效率
c++·set·map
清灵xmf3 个月前
从 Set、Map 到 WeakSet、WeakMap 的进阶之旅
前端·javascript·set·map·weakset·weakmap
DARLING Zero two♡3 个月前
C++效率掌握之STL库:map && set底层剖析及迭代器万字详解
c++·stl·set·map