Files
java-book/2.javaSE进阶/day04/day04【Map、补充知识点】.md
2025-08-27 15:08:08 +08:00

606 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# day04 【Map、补充知识点】
### 主要内容
- Map集合
- 补充知识点
### 教学目标
- [ ] 能够说出Map集合特点
- [ ] 使用Map集合添加方法保存数据
- [ ] 使用”键找值”的方式遍历Map集合
- [ ] 使用”键值对”的方式遍历Map集合
- [ ] 能够使用HashMap存储自定义键值对的数据
- [ ] 能够使用可变参数
- [ ] 能够使用debug调试程序
- [ ] 能够理解冒泡排序的原理
- [ ] 能够使用Arrays数组工具类的常用方法
- [ ] 能够使用HashMap编写斗地主洗牌发牌案例
## 第一章 Map集合
### 1.1 概述
现实生活中我们常会看到这样的一种集合IP地址与主机名身份证号与个人系统用户名与系统用户对象等这种一一对应的关系就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象`java.util.Map`接口。
我们通过查看`Map`接口描述,发现`Map`接口下的集合与`Collection`接口下的集合,它们存储数据的形式不同,如下图。
![](img\Collection与Map.bmp)
* `Collection`中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
* `Map`中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
* `Collection`中的集合称为单列集合,`Map`中的集合称为双列集合。
* 需要注意的是,`Map`中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
### 1.2 Map的常用子类
通过查看Map接口描述看到Map有多个子类这里我们主要讲解常用的HashMap集合、LinkedHashMap集合。
* **HashMap\<K,V>**存储数据采用的哈希表结构元素的存取顺序不能保证一致。由于要保证键的唯一、不重复需要重写键的hashCode()方法、equals()方法。
* **LinkedHashMap\<K,V>**HashMap下有个子类LinkedHashMap存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致通过哈希表结构可以保证的键的唯一、不重复需要重写键的hashCode()方法、equals()方法。
> tipsMap接口中的集合都有两个泛型变量\<K,V>,在使用时,要为两个泛型变量赋予数据类型。两个泛型变量\<K,V>的数据类型可以相同,也可以不同。
>
### 1.3 Map的常用方法
Map接口中定义了很多方法常用的如下
* `public V put(K key, V value)`: 把指定的键与指定的值添加到Map集合中。
* `public V remove(Object key)`: 把指定的键 所对应的键值对元素 在Map集合中删除返回被删除元素的值。
* `public V get(Object key)` 根据指定的键在Map集合中获取对应的值。
* `public Set<K> keySet()`: 获取Map集合中所有的键存储到Set集合中。
* `public Set<Map.Entry<K,V>> entrySet()`: 获取到Map集合中所有的键值对对象的集合(Set集合)。
* `public boolean containKey(Object key)`:判断该集合中是否有此键。
Map接口的方法演示
~~~java
public class MapDemo {
public static void main(String[] args) {
//创建 map对象
HashMap<String, String> map = new HashMap<String, String>();
//添加元素到集合
map.put("黄晓明", "杨颖");
map.put("文章", "马伊琍");
map.put("邓超", "孙俪");
System.out.println(map);
//String remove(String key)
System.out.println(map.remove("邓超"));
System.out.println(map);
// 想要查看 黄晓明的媳妇 是谁
System.out.println(map.get("黄晓明"));
System.out.println(map.get("邓超"));
}
}
~~~
> tips:
>
> 使用put方法时若指定的键(key)在集合中没有则没有这个键对应的值返回null并把指定的键值添加到集合中
>
> 若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。
### 1.4 Map的遍历
#### 方式1:键找值方式
通过元素中的键,获取键所对应的值
分析步骤:
1. 获取Map中所有的键由于键是唯一的所以返回一个Set集合存储所有的键。方法提示:`keyset()`
2. 遍历键的Set集合得到每一个键。
3. 根据键,获取键所对应的值。方法提示:`get(K key)`
遍历图解:
![](img\Map集合遍历方式一.bmp)
*
#### 方式2:键值对方式
即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
**Entry键值对对象:**
我们已经知道,`Map`中存放的是两种对象,一种称为**key**(键),一种称为**value**(值),它们在在`Map`中是一一对应关系,这一对对象又称做`Map`中的一个`Entry(项)``Entry`将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历`Map`集合时,就可以从每一个键值对(`Entry`)对象中获取对应的键与对应的值。
在Map集合中也提供了获取所有Entry对象的方法
- `public Set<Map.Entry<K,V>> entrySet()`: 获取到Map集合中所有的键值对对象的集合(Set集合)。
获取了Entry对象 , 表示获取了一对键和值那么同样Entry中 , 分别提供了获取键和获取值的方法:
- `public K getKey()`获取Entry对象中的键。
- `public V getValue()`获取Entry对象中的值。
操作步骤与图解:
1. 获取Map集合中所有的键值对(Entry)对象以Set集合形式返回。方法提示:`entrySet()`
2. 遍历包含键值对(Entry)对象的Set集合得到每一个键值对(Entry)对象。
3. 通过键值对(Entry)对象获取Entry对象中的键与值。 方法提示:`getkey() getValue()`
遍历图解:
![](img\Map集合遍历方式二.bmp)
> tipsMap集合不能直接使用迭代器或者foreach进行遍历。但是转成Set之后就可以使用了。
>
### 1.5 HashMap存储自定义类型
练习每位学生姓名年龄都有自己的家庭住址。那么既然有对应关系则将学生对象和家庭住址存储到map集合中。学生作为键, 家庭住址作为值。
> 注意,学生姓名相同并且年龄相同视为同一名学生。
>
编写学生类:
~~~java
public class Student {
private String name;
private int age;
//构造方法
//get/set
@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);
}
}
~~~
编写测试类:
~~~java
public class HashMapTest {
public static void main(String[] args) {
//1,创建Hashmap集合对象。
Map<Student,String> map = new HashMap<Student,String>();
//2,添加元素。
map.put(new Student("lisi",28), "上海");
map.put(new Student("wangwu",22), "北京");
map.put(new Student("wangwu",22), "南京");
//3,取出元素。键找值方式
Set<Student> keySet = map.keySet();
for(Student key: keySet){
String value = map.get(key);
System.out.println(key.toString()+"....."+value);
}
}
}
~~~
* 当给HashMap中存放自定义对象时如果自定义对象作为key存在这时要保证对象唯一必须复写对象的hashCode和equals方法(如果忘记请回顾HashSet存放自定义对象)。
* 如果要保证map中存放的key和取出的顺序一致可以使用`java.util.LinkedHashMap`集合来存放。
### 1.6 LinkedHashMap介绍
我们知道HashMap保证成对元素唯一并且查询速度很快可是成对元素存放进去是没有顺序的那么我们要保证有序还要速度快怎么办呢
在HashMap下面有一个子类LinkedHashMap它是链表和哈希表组合的一个数据存储结构。
~~~java
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.7 Map集合练习
**需求:**
计算一个字符串中每个字符出现次数。
**分析:**
1. 获取一个字符串对象
2. 创建一个Map集合键代表字符值代表次数。
3. 遍历字符串得到每个字符。
4. 判断Map中是否有该键。
5. 如果没有第一次出现存储次数为1如果有则说明已经出现过获取到对应的值进行++,再次存储。
6. 打印最终结果
**方法介绍**
`public boolean containKey(Object key)`:判断该集合中是否有此键。
**代码:**
~~~java
public class MapTest {
public static void main(String[] args) {
//友情提示
System.out.println("请录入一个字符串:");
String line = new Scanner(System.in).nextLine();
// 定义 每个字符出现次数的方法
findChar(line);
}
private static void findChar(String line) {
//1:创建一个集合 存储 字符 以及其出现的次数
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
//2:遍历字符串
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
//判断 该字符 是否在键集中
if (!map.containsKey(c)) {//说明这个字符没有出现过
//那就是第一次
map.put(c, 1);
} else {
//先获取之前的次数
Integer count = map.get(c);
//count++;
//再次存入 更新
map.put(c, ++count);
}
}
System.out.println(map);
}
}
~~~
## 第二章 补充知识点
### 2.1 可变参数
**JDK1.5**之后如果我们定义一个方法需要接受多个参数并且多个参数类型一致我们可以对其简化.
**格式:**
```
修饰符 返回值类型 方法名(参数类型... 形参名){ }
```
**代码演示:**
```java
public class ChangeArgs {
public static void main(String[] args) {
int sum = getSum(6, 7, 2, 12, 2121);
System.out.println(sum);
}
public static int getSum(int... arr) {
int sum = 0;
for (int a : arr) {
sum += a;
}
return sum;
}
}
```
**注意:**
1.一个方法只能有一个可变参数
2.如果方法中有多个参数可变参数要放到最后
**应用场景: Collections**
在Collections中也提供了添加一些元素方法
`public static <T> boolean addAll(Collection<T> c, T... elements) `:往集合中添加一些元素
**代码演示:**
```java
public class CollectionsDemo {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
//原来写法
//list.add(12);
//list.add(14);
//list.add(15);
//list.add(1000);
//采用工具类 完成 往集合中添加元素
Collections.addAll(list, 5, 222, 12);
System.out.println(list);
}
```
### 2.2 Debug追踪
**使用IDEA的断点调试功能查看程序的运行过程** Debug调试窗口介绍
![](img\debug5.png)
### 2.3 数组排序
#### 冒泡排序
##### 冒泡排序概述
- 一种排序的方式对要进行排序的数据中相邻的数据进行两两比较将较大的数据放在后面依次对所有的数据进行操作直至所有数据按要求完成排序
- 如果有n个数据进行排序总共需要比较n-1次
- 每一次比较完毕下一次的比较就会少一个数据参与
##### 冒泡排序图解
![5](img/5.png)
##### 冒泡排序代码实现
```java
/*
冒泡排序:
一种排序的方式,对要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后面,
依次对所有的数据进行操作,直至所有数据按要求完成排序
*/
public class ArrayDemo {
public static void main(String[] args) {
//定义一个数组
int[] arr = {7, 6, 5, 4, 3};
System.out.println("排序前:" + arrayToString(arr));
// 这里减1是控制每轮比较的次数
for (int x = 0; x < arr.length - 1; x++) {
// -1是为了避免索引越界-x是为了调高比较效率
for (int i = 0; i < arr.length - 1 - x; i++) {
if (arr[i] > arr[i + 1]) {
int temp = arr[i];
arr[i] = arr[i + 1];
arr[i + 1] = temp;
}
}
}
System.out.println("排序后:" + arrayToString(arr));
}
//把数组中的元素按照指定的规则组成一个字符串:[元素1, 元素2, ...]
public static String arrayToString(int[] arr) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
if (i == arr.length - 1) {
sb.append(arr[i]);
} else {
sb.append(arr[i]).append(", ");
}
}
sb.append("]");
String s = sb.toString();
return s;
}
}
```
### 2.4 Arrays类
#### 概述
`java.util.Arrays` 此类包含用来操作数组的各种方法比如排序和搜索等其所有方法均为静态方法调用起来非常简单
#### 操作数组的方法
- `public static String toString(int[] a) ` 返回指定数组内容的字符串表示形式
```java
public static void main(String[] args) {
// 定义int 数组
int[] arr = {7, 6, 5, 4, 3};
// 打印数组,输出地址值
System.out.println(arr); // [I@2ac1fdc4
// 数组内容转为字符串
String s = Arrays.toString(arr);
// 打印字符串,输出内容
System.out.println(s); // [7, 6, 5, 4, 3]
}
```
- `public static void sort(int[] a)` 对指定的 int 型数组按数字升序进行排序
```java
public static void main(String[] args) {
// 定义int 数组
int[] arr = {7, 6, 5, 4, 3};
System.out.println("排序前:"+ Arrays.toString(arr)); // 排序前:[7, 6, 5, 4, 3]
// 升序排序
Arrays.sort(arr);
System.out.println("排序后:"+ Arrays.toString(arr));// 排序后:[3, 4, 5, 6, 7]
}
```
## 第三章 模拟斗地主洗牌发牌
### 3.1 案例介绍
按照斗地主的规则完成洗牌发牌的动作
![](img\斗地主.png)
具体规则
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)集合,完成一个数字与字符串纸牌的对应关系(相当于一个字典)。
2. 洗牌:
通过数字完成洗牌发牌
3. 发牌:
将每个人以及底牌设计为ArrayList\<String>,将最后3张牌直接存放于底牌剩余牌通过对3取模依次发牌。
存放的过程中要求数字大小与斗地主规则的大小对应。
将代表不同纸牌的数字分配给不同的玩家与底牌。
4. 看牌:
通过Map集合找到对应字符展示。
通过查询纸牌与数字的对应关系,由数字转成纸牌字符串再进行展示。
![](img\斗地主分析.png)
### 3.3 实现代码步骤
~~~java
package com.inmind04;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
/*
* 组合牌
* 定义一个Map集合用来存储牌号 和 牌
* 定义一个List集合用来存储牌号
* 花色:♥-♠-♦-♣
* 数字:2-A-K-Q-J-10-9-8-7-6-5-4-3
* 洗牌
* Collections.shuffle(牌号集合)
* 发牌
* 三个玩家三个集合
* 发牌号
* 排序
* 看牌
*/
public class Pooker {
public static void main(String[] args) {
// 定义一个Map集合用来存储牌号 和 牌
HashMap<Integer, String> pookerMap = new HashMap<Integer, String>();
//定义一个List集合用来存储牌号
ArrayList<Integer> pookerList = new ArrayList<Integer>();
String[] colors = "♥-♠-♦-♣".split("-");
String[] nums = "2-A-K-Q-J-10-9-8-7-6-5-4-3".split("-");
int index = 2;
for(String num : nums){
for(String color : colors){
String thisPooker = color+num;
// System.out.println(thisPooker);
//将扑克牌放入Map集合
pookerMap.put(index, thisPooker);
//将牌号放入到pookerList集合中
pookerList.add(index);
index++;
}
}
//将大王小王添加到集合
pookerMap.put(0, "大王");
pookerMap.put(1, "小王");
pookerList.add(0);
pookerList.add(1);
// System.out.println(pookerMap);
// System.out.println(pookerList);
//洗牌
Collections.shuffle(pookerList);
//发牌
ArrayList<Integer> player1 = new ArrayList<Integer>();
ArrayList<Integer> player2 = new ArrayList<Integer>();
ArrayList<Integer> player3 = new ArrayList<Integer>();
ArrayList<Integer> diPai = new ArrayList<Integer>();
//遍历牌号的集合 判断索引发牌号
for(int i = 0 ;i < pookerList.size() ;i++){
Integer pookerNum = pookerList.get(i);
if(i>=51)
diPai.add(pookerNum);
}else if(i % 3 == 0){
player1.add(pookerNum);
}else if(i % 3 == 1){
player2.add(pookerNum);
}else if(i % 3 == 2){
player3.add(pookerNum);
}
}
// 排序
Collections.sort(player1);
Collections.sort(player2);
Collections.sort(player3);
Collections.sort(diPai);
// System.out.println(player1);
// System.out.println(player2);
// System.out.println(player3);
// System.out.println(diPai);
show("柳岩",player1,pookerMap);
show("唐嫣",player2,pookerMap);
show("金莲",player3,pookerMap);
show("底牌",diPai,pookerMap);
}
//定义方法 看牌
public static void show(String name,ArrayList<Integer> player,HashMap<Integer, String> pookerMap ){
System.out.print(name+":");
for(Integer pookerNum : player){
String thisPooker = pookerMap.get(pookerNum);
System.out.print(thisPooker+" ");
}
System.out.println();
}
}
~~~