目录
前言:
本篇文章将会结合三道题目,由浅入深,逐步讨论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,本篇文章便到此结束!

夏日的暖阳炽热而温和;
似波,似水,似千秋万浪!