集合框架
数组: 类型确定长度固定 length 房间数量可以跨房间赋值
集合: 默认Object类型长度可以自由增长size()元素个数必须依次赋值
架构
Collection
List<>子接口有序集合(有序:展示顺序与保存顺序一致)==》排序
特点:有索引可以存储重复值可以使用普通for循环、增强for循环(迭代器)
ArrayListLinkedListVector
Set<>子接口无序集合(无序:展示顺序与保存顺序不一定一致)==》去重
特点:无索引 不可以存储重复值只能使用增强for循环(迭代器)
HashSetTreeSet
LinkedHashSet
Collection单列集合父接口
booleanadd(E)添加元素
booleanremove(E)移除元素 只移除第一次查找到的 根据hashCode() equals() 判定重复
booleanremoveIf(Predicate)有条件删除某些元素
参数为一个接口 要实现其中的test() 返回true则当前元素被删除 返回false被保留
voidclear()清除所有元素
booleanisEmpty()判断是否有元素
booleancontains(E)判断某个元素是否存在 根据hashCode() equals()
intsize()返回元素个数
booleanaddAll(Collection)存入一个小集合
booleanremoveAll(Collection)从大集合中 移除参数集合中所有的符合要求元素
booleancontainsAll(Collection)从大集合中 判断是否包含小集合中的所有元素
Iteratoriterator()获取当前集合的正向迭代器 增强for循环的前身
Object数组类型toArray()转数组
数组类型toArray(数组)转数组
//数组转集合List
//必须准备引用数据类型的数组
Integer[] nums = new Integer[cc.size()];
cc.toArray(nums);
System.out.println(Arrays.toString(nums));
Object[] array = cc.toArray();
//int[] n = (int[])array; //不能直接强转数组类型
int[] n = new int[array.length];
//需要通过循环 每个元素 单独强转
for(int i =0;i<array.length;i++) {
n[i] = (int)array[i];
}
//数组转集合 要求 数组必须是引用数据类型
Double[] dd = new Double[] {3.5,7.8,3.14,8.9};
List<Double> asList = Arrays.asList(dd);
for(double d:asList) {
System.out.print(d+" ");
}
练习
创建Pet宠物类String nameint age
创建ArrayList集合循环存储多个宠物对象 询问是否继续 Collection
输入宠物昵称 判断是否存在 存在则将年龄+2
输入宠物昵称和年龄 构成一个宠物对象 判断集合中是否存在相同属性值的对象
输入宠物昵称 判断是否存在 存在则删除这个宠物对象(提示 隐藏方法)
通过迭代器展示所有宠物信息
将该集合转为数组 展示
一个Object集合 删除所有int类型的元素
删除User集合中所有年龄>20岁的人
ArrayList<User> au = new ArrayList<User>();
au.removeIf(new PreImpl());//removeIf方法会循环将每一个元素 传入接口实现类中的test()进行判断
//单独创建一个类 实现 Predicate接口中的test() 返回true会被删除 否则被保留
public class PreImpl implements Predicate<User>{
public boolean test(User u){
return u.getAge()>20;
}
}
//使用局部内部类技术 将接口外部实现类 优化掉
Predicate<User> pu = new Predicate<User>(){
public boolean test(User u){
return u.getAge()>20;
}
};
au.removeIf(pu);
//因为Predicate接口是一个函数式接口(只有唯一一个【抽象方法】的接口) @FunctionalInterface
//可以使用lambda表达式 再一次优化
Predicate<User> pu = (User u)->{ return u.getAge()>20; };
au.removeIf(pu);
Predicate<User> pu = u->u.getAge()>20 ;
au.removeIf(pu);
au.removeIf(u->u.getAge()>20);
泛型
当不确定类型时 使用泛型占位
泛型的赋值 必须使用引用数据类型 (包装类)
泛型受限
当在代码的中间环节 还不确定泛型类型时 可以使用?
泛型受限(泛型边界) 控制将来泛型类型的取值范围
public class Demo1{
}
public class Demo2<E> extends Demo1{
}
public class Demo3 extends Demo2{
}
public class Impl {
//Collection<Demo1> cd1 = new ArrayList<Demo1>();
//cd1.add(new Demo1());
//cd1.add(new Demo2());
//cd1.add(new Demo3());
public void show1(Collection<Demo1> c) { //只能传入 Collection<Demo1>
//集合中的元素 可以是 Demo1对象 Demo2对象 Demo3对象
}
//泛型受限 边界
public void show2(Collection<? extends Demo2> c) {
//可以传入 Collection<Demo2> demo2 demo3
//Collection<Demo3> Demo3对象
}
public void show3(Collection<? super Demo2> c) {
//可以传入 Collection<Demo2> Collection<Demo1>
}
}
//测试类1
Impl impl = new Impl();
Collection cd = new ArrayList();
Collection<Demo1> cd1 = new ArrayList<Demo1>();
cd1.add(new Demo1());
cd1.add(new Demo2());
impl.show1(cd1);//不能放入Demo2 Demo3集合
Collection<Demo2> cd2 = new ArrayList<Demo2>();
// cd2.add(new Demo1());
// cd2.add(new Demo2());
// cd2.add(new Demo3());
// impl.show1(cd2);
Collection<Demo3> cd3 = new ArrayList<Demo3>();
impl.show2(cd2);//可以放入 cd2 cd3
impl.show3(cd2);//可以放入 cd2 cd1
ArrayList
底层是一个数组模型默认长度为10
private static final int DEFAULT_CAPACITY = 10; //默认数组长度
private static final Object[] EMPTY_ELEMENTDATA = {}; //当声明时定义长度为0 则使用该数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//默认10长度空参构造
transient Object[] elementData; //真正使用的数组
private int size; //集合元素个数
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //可以存储的最大元素数量
public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
//空参构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//带int类型参数的构造 定义数组默认长度
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//带单列集合实参的构造方法 可以将其他形式的单列集合 转为ArrayList集合
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
//当空间不足时 添加元素会执行扩容方法 每次默认扩容为原来的1.5倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
优点:数组模型 元素是连续的 可以直接通过下标获取某个元素===》查询快
缺点:1.每次扩容 老数组成垃圾数据
2.新增 删除 都需要元素移动===》增删慢
常用方法
voidadd(int index, E element) index>=0 index<=size()
boolean addAll(int index, Collection<? extends E> c)
Eremove(int index) 返回被删除的元素
Eset(int index, E element) 使用第二个参数 替换第一个参数下标位的元素 返回被替换的老元素
第一个表示下标的参数 必须存在 >=0&&<长度
Eget(int index) //数组名[下标]
intindexOf(Object o)根据元素对象的hashCode() equals()判断 找不到 返回 -1
ListIteratorlistIterator()//双向迭代器 默认从头号元素开始迭代
ListIteratorlistIterator(int index) 从某个下标位开始向左迭代 应该存放起始下标+1
如果向右迭代 则正常放起始下标
迭代器Iterator
每个单列集合都有自己的迭代器 泛型就是集合元素的类型
booleanhasNext()判断有没有下一个元素
Enext()获取下一个元素 并且模拟指针下跳
voidremove()删除当前元素
1.使用迭代器删除元素 一定要使用迭代器自己的删除方法 不要使用集合删除 否则并发异常
2.每次循环独立创建一个新的迭代器 不要共用 担心指针已经不正确
3.迭代器获取元素与增强for循环一样 都是获取集合中元素的地址
4.一个hasNext() 配 一个next()
//正向迭代器
//每一个集合 都有一个自己的迭代器对象
Iterator<String> it = as.iterator();
while(it.hasNext()) { //有没有下一个元素 返回boolean
String name = it.next(); //获取下一个元素 同时模拟指针下跳 //获取下一个元素
//System.out.println(name);
if(name.equals("李四") || name.equals("马六")) {
//as.remove(name); //不能一边迭代 一边用集合删除元素 会报并发异常
//ConcurrentModificationException
it.remove(); //迭代器自身的删除方法
}
}
Iterator<User> iu = cu.iterator();
while(iu.hasNext()){
User u = iu.next();//u与当前元素共享地址
//u = new User("",18);//u指向新地址 与原元素没有关系
u.name = "李四";
u.age = 100; //通过u修改属性 原元素也会一起修改
}
增强for
增删不可用
修改
基本数据类型 不可用
引用数据类型
修改地址 不可用
修改属性 可用
迭代器
增 不可用
删 可用==》要用迭代器对象调用remove()
修改
基本数据类型 不可用
引用数据类型
修改地址 不可用
修改属性 可用
子类 双向迭代器 ListIterator
ListIterator
ListIterator
如果走正向迭代 则能够获取参数下标位
如果走逆向迭代 则从参数下标位的上一位开始
只能用于有序集合
booleanhasNext()判断有没有下一个元素
Enext()获取下一个元素 并且模拟指针下跳
booleanhasPrevious()判断有没有上一个元素
Eprevious()获取上一个元素
voidremove()删除当前元素
ArrayList<Integer> ai = new ArrayList<Integer>();
ai.add(56);
ai.add(77);
ai.add(26);
ai.add(78);
ai.add(15);
ai.add(16);
ListIterator<Integer> lit = ai.listIterator(ai.size());
while(lit.hasPrevious()) {
int num = lit.previous();
System.out.print(num+" ");
}
LinkedList
双链结构===》查询慢 增删快
//在LinkedList内 有一个内部类 表示每一个节点对象
private static class Node<E> {
E item; //当前节点的值
Node<E> next; //保存该节点下一个节点对象
Node<E> prev; //保存该节点上一个节点对象
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
//空参构造
public LinkedList() {
}
//带参构造
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
常用方法
voidaddFirst(E e)
voidaddLast(E e)
EgetFirst()
EgetLast()
EremoveFirst()
EremoveLast()
Vector
线程安全 效率低 JDK1.0
可以使用Iterator ListIterator 也可以使用独有的一套迭代器
Vector<Integer> vi = new Vector<Integer>();
vi.add(123);
vi.add(666);
vi.add(35);
vi.add(123);
vi.add(789);
vi.add(123);
Enumeration<Integer> e = vi.elements();
while(e.hasMoreElements()) {
Integer num = e.nextElement();
System.out.print(num+" ");
}
Iterator<Integer> ii = vi.iterator(); ctrl+1
while(ii.hasNext()){
syso(ii.next());
}
ctrl+shift+o
比较器 接口
案例
每个元素都是字母 的String类型的集合 希望升序排列 再 【降序排列】
优先根据姓名的升序 姓名相同 根据年龄的降序 给用户添加排序的选项 【1. 2.年龄升序 姓名降序】
内部比较器
Comparable
要比较的实体类直接实现
public class User implements Comparable<User>{
private String name;
int ae;
double score;
public User(String name, int ae,double score) {
super();
this.name = name;
this.ae = ae;
this.score = score;
}
public String getName(){
return name;
}
@Override
public String toString() {
return "User [name=" + name + ", ae=" + ae + "]";
}
@Override
public int compareTo(User o) {
// TODO Auto-generated method stub
//年龄升序
//return this.ae-o.ae;
//姓名降序
//return o.name.compareTo(this.name);
//.姓名升序 姓名相同 年龄升序
// if(this.name.equals(o.name)) {
// return this.ae - o.ae;
// }
// return this.name.compareTo(o.name);
//年龄降序 年龄相同 姓名升序
if(this.ae == o.ae) {
return this.name.compareTo(o.name);
}
return o.ae-this.ae;
//根据积分的升序
return this.score>o.score?1:-1; return Double.compare(this.score,o.score);
}
}
//测试类中
ArrayList<User> au = new ArrayList<User>();
au.add(new User("jdfg",18));
au.add(new User("kert",21));
au.add(new User("zw",18));
au.add(new User("jdfg",22));
au.add(new User("jdfg",18));
au.add(new User("jdywrfg",18));
au.add(new User("jdfg",19));
//sort() 负责将数组或者集合两两元素以冒泡排序的形式 传给比较器算法 接收算法返回的整数 决定交换与否
Collections.sort(au); //该集合必须有序 如果元素所属类没有实现内部比较器 则该行代码报错
for(User u : au) {
System.out.println(u);
}
外部比较器
Comparator
单独创建一个类来实现
1.外部比较器可以有多个 随意选择
2.不影响实体类结构
3.外部比较器优先级更高 可以覆盖内部比较器
//外部比较器1
//单独创建一个类 实现外部比较器
public class Ucom1 implements Comparator<User> {
//姓名升序
@Override
public int compare(User o1, User o2) {
// TODO Auto-generated method stub
return o1.name.compareTo(o2.name);
}
}
//外部比较器4
public class Ucom4 implements Comparator<User> {
//年龄降序 年龄相同 姓名升序
@Override
public int compare(User o1, User o2) {
// TODO Auto-generated method stub
if(o1.ae==o2.ae) {
return o1.name.compareTo(o2.name);
}
return o2.ae-o1.ae;
}
}
//直接在要使用比较器的类中 以内部类的形式 创建外部比较器的实现类的对象
//外部比较器2
Comparator<User> cu2 = new Comparator<User> {
//年龄降序
@Override
public int compare(User o1, User o2) {
// TODO Auto-generated method stub
return o2.ae-o1.ae;
}
}
//测试类中
ArrayList<User> au = new ArrayList<User>();
au.add(new User("jdfg",18));
au.add(new User("kert",21));
au.add(new User("zw",18));
au.add(new User("jdfg",22));
au.add(new User("jdfg",18));
au.add(new User("jdywrfg",18));
au.add(new User("jdfg",19));
System.out.println("1.姓名升序 2.年龄降序 3.姓名升序 姓名相同 年龄升序 4.年龄降序 年龄相同 姓名升序");
int sel = new Scanner(System.in).nextInt();
if(sel==1) {
Collections.sort(au, new Ucom1());
}else if(sel==2) {
//采用内部类 优化掉外部类
Collections.sort(au, cu2);
}else if(sel==3) {
//采用lambda 表达式 + 匿名对象 直接将外部比较器的实现类对象 传入sort方法
Collections.sort(au, (o1,o2)->o1.name.equals(o2.name)?o1.ae-o2.ae:o1.name.compareTo(o2.name));
}else {
Collections.sort(au, new Ucom4());
}
for(User u : au) {
System.out.println(u);
}
//局部内部类 优化掉 接口实现类对象 并使用lambda表达式 优化函数式接口
Collections.sort(au,(o1,O2)-> o1.ae-o2.ae);
//au.sort((o1,o2)->o1.age);
//au.sort();//报错
int[] nums = {5,3,8,9,1,6,4};
Arrays.sort(nums);
Arrays.sort(nums,(n1,n2)->n2-n1);//降序
User[] us = new User[au.size()];
au.toArray(us);
Arrays.sort(us,(o1,o2)-> o1.age==o2.age? o2.score>o1.score?1:-1 : o1.age-o2.age);
案例
int类型的集合 升序排列 降序排列
String 类型的集合 默认是升序排列 如何降序排列呢
User类 全英文name属性 double类型的成绩
存入几个用户对象 按照以下要求排序展示
1.姓名升序 2.成绩降序 3.姓名升序 姓名相同 成绩升序 4.成绩降序 成绩相同 姓名升序
Stream 数据流式操作
Stream
Stream
Stream
String Arrays.toString(数组)
新数组Arrays.copyOf(老数组,新长度)
voidArrays.sort(可排序数组)元素类型有内部比较器 int[] nums String[] ss
voidArrays.sort(不可排序数组,外部比较器)
List
Stream
常用方法
interface Predicate
interface Comparator
interface Consumer
interface Function<E,T>Tapply(E o)
filter(interface Predicate
sorted()对于流中的元素 采用对象的内部比较器进行排序
sorted(interface Comparator
void foreach(interface Consumer
longcount()统计流中有几行数据
map(Function<? super Worker, ? extends String> mapper) map( o->o.getName()).filter(o->o.equals("张三"))
提出元素中的某个属性 成为新的数列 类型也将变为属性的类型
limit(long)获取当前流的前几行
skip(long)跨过流的前几行
boolean anyMatch(interface Predicate
boolean allMatch(interface Predicate
distinct()去除流中的重复元素 判断条件 是 hashCode() equals()
List
ArrayList<User> au = new ArrayList<User>();
au.add(new User());
...
//原始集合经过处理后获得的数据 可以继续通过.collect(Collectors.toList())转为List集合
List<User> lu = au.stream().filter()... .collect(Collectors.toList());
//再使用集合构造方法 转为子实现类集合
ArrayList<User> auu = new ArrayList<User>(lu);
无序集合Set(子接口)
展示顺序与存储顺序 未必一致 但是一定是一个固定的顺序(根据地址计算出的顺序)
不能存储重复数据 不能使用普通for循环
案例
为什么int类型的无序集合可以自动去重 Integer
因为int的包装类中重写了hashCode()和equals()方法
总结
java中是根据hashCode()和equals()共同作用 实现去重的
String str1 = new String("重地");
String str2 = new String("通话");
System.out.println(str1.hashCode()); //1179395
System.out.println(str2.hashCode()); //1179395
public class User{
String name;
...
//重写了hashCode() equals() 勾选了name属性
}
//测试类中
HashSet<User> hu = new HashSet<User>();
User u = new Uesr("aaa",18);
hu.add(u);
//hu.add(u);//无法存入 不是因为地址相同 是因为name相同
u.name = "bbb";
hu.add(u); //可以存入 因为第一次存的u的hash值时根据aaa计算 本次是根据bbb计算 hash值不同 因此可存
syso(hu);
/*
会看到两个bbb用户 因为毕竟是同一个对象 改变属性则一起变化
*/
基本数据类型、String默认去重==>这些类重写了HashCode() equals()
如果要保存相同的字符串 则可以使用String增强类
自定义类型集合如果没有重写hashCode()和equals() 那么直接判断地址是否相同
如果重写了 则判断重写的方法中所判断的属性
LinkedHashSet
是HashSet的子类多了一根链条 记录了保存顺序 ==》展示顺序就是存储顺序
//User类重写了hashCode() equals() 只判断姓名
//该集合会根据姓名去重 展示顺序与存储顺序一致
LinkedHashSet<User> lhu = new LinkedHashSet<User>();
lhu.add(new User("张三",18));
lhu.add(new User("jsdfg",18));
lhu.add(new User("jsdfg",28));
lhu.add(new User("jsdfg",19));
lhu.add(new User("张三",19));
//只存储了 第一个 第二个 对象
User u = new User("李四",22);//根据李四 算出 哈希值 842092
lhu.add(u);
u.name = "王豆豆";
syso(u.hashCode());//当前u的hash值 变为29574570
lhu.add(u); //可以存入 因为已经存入的李四 虽然姓名变成“王豆豆” 但是hash值不会改变
lhu.stream().forEach(o->System.out.println(o));
//最后会有两个王豆豆
HashSet<StringBuilder> sb = new HashSet<StringBuilder>();
sb.add(new StringBuilder("aaa"));
sb.add(new StringBuilder("aaa"));
sb.add(new StringBuilder("aaa"));
sb.add(new StringBuilder("aaa"));
保存了四个元素
HashSet<String> ss = new HashSet<String>();
ss.add(new String("aaa"));
ss.add(new String("aaa"));
ss.add(new String("aaa"));
ss.add(new String("aaa"));
只保存了一个值
TreeSet
存储数据的过程必须依赖于比较器 ==》根据比较器返回值实现去重
返回0则认为是重复数据 返回其他任何数据都认为是不重复数据
基本数据类型、String因为已经实现了Comparable接口 则可以通过该比较器去重
自定义类型一定要给与其比较器 否则存储对象报错 根据比较器结果决定是否重复和排序
注意:1.使用TreeSet存储的元素类型 必须有比较器 否则存储失败
2.比较器返回0 统统认为是重复数据 返回1 都被认定为不重复 返回-1不仅不重复且逆向保存
3.TreeSet因为其底层数据模型 因此 去重和排序 不是特别稳定
希望 使用无序集合 保存重复的字符串
public class User implements Comparable<User>{
String name;
int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
@Override
public int compareTo(User o) {
// TODO Auto-generated method stub
//return this.name.equals(o.name)?0:1;
if(this.name.equals(o.name)) {
return o.age-this.age;
}
return this.name.compareTo(o.name);
}
}
//0.全部存入 按姓名升序 姓名相同 年龄升序
(o1,o2)->o1.name.equals(o1.name)? o1.age==o2.age?1:o1.age-o2.age : o1.name.compareTo(o2.name)
//1.只根据姓名去重 再根据年龄升序排列
(o1,o2)->o1.name.equals(o2.name)? 0 : o1.age==o2.age ? 1 : o1.age-o2.age
//2.去除年龄相同的用户 并按照姓名升序 姓名相同 年龄降序 排列用户
(o1,o2)->o1.age==o2.age? 0 : o1.name.equals(o2.name)?o2.age-o1.age:o1.name.compareTo(o2.name)
//测试类中
TreeSet<User> hu = new TreeSet<User>();
hu.add(new User("bbb",18));
hu.add(new User("bbb",23));
hu.add(new User("aaa",28));
hu.add(new User("bbb",20));
hu.add(new User("ccc",18));
hu.add(new User("aaa",28));
hu.add(new User("bbb",19));
hu.add(new User("aaa",21));
hu.add(new User("aaa",25));
hu.add(new User("bbb",22));
hu.add(new User("aaa",28));
hu.stream().forEach(o->System.out.println(o));
TreeSet<String> ts = new TreeSet<String>((o1,o2)->1);
ts.add(new String("aaa");
ts.add("aaa");
ts.add(new String("aaa"));
ts.add("aaa");
保存四个元素
案例
保存五个5==》编写外部比较器 放入构造方法 可以覆盖内部比较器
TreeSet<Integer> ti = new TreeSet<Integer>((o1,o2)->-1);
ti.add(5);
ti.add(5);
ti.add(5);
ti.add(5);
ti.add(5);
System.out.println(ti); //[5,5,5,5,5]
双列集合Map<K,V>
HashMap<K,V>
每一组数据 由 键和值 组成 ===》键值对
键不能重复可以为null只能有一个null键
值可以重复 可以为null可以有多个null值
当存入重复键的键值对时 执行的是覆盖操作
常用方法
intsize()键值对数量
put(k,v)添加一组键值对如果添加了重复键的键值对 则执行的是替换操作
v get(k)通过键找值 找不到 返回null
vremove(k)直接删除 返回被删除的键值对的值 删除键值对 找不到 返回null
boolean remove(k,v)先判断 再删除
vreplace(k,newV)直接替换旧值 返回旧值
booleanreplace(k,oldV,newV)先判断 再替换
Set
Collection
booleancontainsKey(k)判断是否包含某个键
booleancontainsValue(V)判断是否包含某个值
Set<Entry<K,V>> entrySet()获取Map集合的 所有键值对 所组成的映射关系集合
Entry<K,V>映射关系类
KgetKey()获取当前映射关系中的键
VgetValue()获取当前映射关系中的值
voidsetValue(V)给当前映射关系中的值重新赋值
String toString() 键=值
注意:
通过键的集合 删除某个键 会导致HashMap集合中该键值对被删除
通过Entry集合 修改或者删除某个键值对 也会导致HashMap集合中该键值对的修改或删除
评论