原创不易,转载请注明出处https://blog.csdn.net/weixin_43330377/article/details/109706045
今天写完了实验四Java集合与泛型的实验报告,发现自己对HashMap中equals和Hashcode理解不深,因此查阅资料后做出如下总结,以及在实验过程中出现的问题。
加深印象,便于复习
【实验总结】
- 1.关于java中的三大容器List、Map、Set的详解不加以叙述。
但要注意的是,List有Index索引,Map有Key键,根据index和Key的不同,同一个对象或者Value可以存放在不同的位置(有序),但Set由于没有相应的唯一标识,故不准许添加相同的内容(无序:通过equals和Hashcode进行判断,同时相等,则判定为相同对象,若不同,则判定为不同的对象。)。
equals和Hashcode方法及重写的理解与总结
- 2.“由Teacher可以根据id比大小”引发的对于equals和Hashcode方法及重写的理解与总结
(1)思路:由于HashMap根据Key的HashCode进行排序返回,我们可以根据当equals()都返回True时,根据Key中Teacher 类ID的HashCode的不同进行区分排序。
(2)代码如下:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Teacher)) return false;
Teacher teacher = (Teacher) o;
return getId() == teacher.getId();
}
@Override
public int hashCode() {
return Objects.hash( getId());
}
}
(3)注意事项!!!!!
判断相等的过程是:先判断两个对象的HashCode的值是否相等,若相等,再进行equals()判断。若HashCode不相等,则判断两个对象不相等,直接加入到容器中,不再进行equals()比较。
两个方法必须同时重写
如果仅重写HashCode,系统调用的是Object 类中的equals()方法,直接调用“==”,比较的是对象的内存地址。
源码如下:
public boolean equals(Object obj) {
return (this == obj);
}
当我们用new创建的对象和数组的时候,尽管内容完全一样,但其相应的堆内存却不同
如: teacher1 teacher2尽管完全相同,但返回的地址却不同。
Teacher teacher1=new Teacher("罗老师",001,"C++","Java");
Teacher teacher2=new Teacher("罗老师",001,"C++","Java");
因为重写了HashCode,所以他们的HashCode的值相同,但equals却返回false,会发生Hash碰撞,也就是两个不同对象的HashCode相同,Hashmap会试图把他们保存在同一个位置,又无法成功。就会在相应位置用链式结构保存多个对象,通过遍历链表进行equals()最终找到要找的值对象。
不但违背了Set容器的规则,而且又使性能下降。
如果仅仅重写equals(),系统调用的是Object 类中的HashCode()方法,hashCode返回的是由对象存储地址转化得到的值。
上述例子已经表明相同内容,不同对象返回的地址不同,所以通常情况下HashCode不同,则不会继续进行equals的判断,直接将元素加入到相应位置中,违背了Set容器的规范。(编译运行不会报错。)
注:当返回的hash码产生溢出时,也可能会出现不同对象hashcode相同的情况
- 3.indexof():查找一个字符串中,首次出现该字符串位置的索引,若没有出现则返回-1。
get():List中的第i+1(对应的索引值)个对象
最开始的时候我将这两个方法混淆,用Indexof(Index)找对象,结果都返回为-1,表明该对象不存在。
因此若对象明明存在ArrayList中却依旧返回-1,可能是Indexof()方法使用错误。 - 4.错误:Operator ‘+’ cannot be applied to ’ ', ’ ’
解决办法: 两个对象之间加个分隔符就好了。 - 5 .ArrayList是在第一次添加元素时容量扩大至 10 的,故说其初始容量为10。当超过容量时,可以动态扩容。
附:【实验内容】
定义类 Teacher
属性:
String name;
int id;
String cpp;( 教授课程1)
String java; ( 教授课程2)
方法:根据需要自定。
所有集合要求用泛型
1、编写程序练习List集合的基本使用:
(1) 创建一个只能容纳Teacher类型的对象的ArrayList集合;
(2) 按顺序往集合中添加5个Teacher对象;
(3) 对集合进行遍历,分别打印集合中的每个元素的位置与内容;
(4) 首先打印集合的大小,然后删除集合中的第3个元素,并显示删除元素的内容,再次打印集合的大小。
2、编写程序练习Map集合的基本使用:
(1) 创建一个HashMap集合,要求以Teacher作为键(Teacher可以根据id比大小),同样以Teacher作为值;
(2) 往集合中添加5个“键-值”对象;
(3) 对集合进行遍历,分别打印集合中的每个元素的键与值;
(4) 获得集合中的所有键的集合,并进遍历;
(5) 用entrySet获取键值对,并进行遍历。
(6) 练习使用Map.Entry,自己写一个测试。
【实验代码】
Teacher .java
import java.util.Objects;
public class Teacher {
String name;
int id;
String cpp;//( 教授课程1)
String java; //( 教授课程2)
public Teacher(String name, int id, String cpp, String java) {
this.name = name;
this.id = id;
this.cpp = cpp;
this.java = java;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", id=" + id +
", cpp='" + cpp + '\'' +
", java='" + java + '\'' +
'}';
}//根据ID打印
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Teacher)) return false;
Teacher teacher = (Teacher) o;
return getId() == teacher.getId();
}
@Override
public int hashCode() {
return Objects.hash( getId());
}
}
TeacherTest1.java(List)
import java.util.ArrayList;
import java.util.List;
public class TeacherTest1 {
public static void main(String[] args) {
/* 创建一个只能容纳Teacher类型的对象的ArrayList集合;*/
List<Teacher> teacherList= new ArrayList();
Teacher teacher1=new Teacher("罗老师",001,"C++","Java");
Teacher teacher2=new Teacher("宋老师",002,"计算机组成原理","单片机");
Teacher teacher3=new Teacher("张老师",003,"操作系统","软件测试");
Teacher teacher4=new Teacher("吴老师",004,"数据库原理","SQL");
Teacher teacher5=new Teacher("邓老师",005,"信息系统分析与设计","软件工程");
/* 按顺序往集合中添加5个Teacher对象;*/
teacherList.add(0,teacher1);
teacherList.add(1,teacher2);
teacherList.add(2,teacher3);
teacherList.add(3,teacher4);
teacherList.add(4,teacher5);
/*对集合进行遍历,分别打印集合中的每个元素的位置与内容; */
/*lambda表达式*/
System.out.println("---------------------------");
teacherList.forEach((i)-> {
System.out.println("元素:"+i);
System.out.println("位置:"+teacherList.indexOf(i));
});
System.out.println("---------------------------");
/*首先打印集合的大小,然后删除集合中的第3个元素,并显示删除元素的内容,再次打印集合的大小。*/
System.out.println("删除前集合的大小为:"+teacherList.size());
System.out.println("被删除的元素为:"+teacherList.get(2) );
//注意这俩放的位置,否则会打印出被删除后相应位置的索引内容
teacherList.remove( 2);
//indexof里面是对象而不是int!!!!!!!!
//indexOf()是在ArrayList中查找某个数据出现的位置索引,如果在ArrayList中没有找到这个数据,就会返回-1
System.out.println("删除后集合的大小为:"+teacherList.size());
}
}
TeacherTest2.java(Map)
import java.util.*;
public class TeacherTest2 {
public static void main(String[] args) {
System.out.println("--------以下为根据ID排序--------");
/* 创建一个HashMap集合,要求以Teacher作为键(Teacher可以根据id比大小),同样以Teacher作为值*/
Map<Teacher, Teacher> teacherHMap = new HashMap();
/*往集合中添加5个“键-值”对象;对集合进行遍历,分别打印集合中的每个元素的键与值*/
Teacher teacher1 = new Teacher("罗老师", 1, "C++", "Java");
Teacher teacher2 = new Teacher("宋老师", 2, "计算机组成原理", "单片机");
Teacher teacher3 = new Teacher("张老师", 3, "操作系统", "软件测试");
Teacher teacher4 = new Teacher("吴老师", 4, "数据库原理", "SQL");
Teacher teacher5 = new Teacher("邓老师", 5, "信息系统分析与设计", "软件工程");
Teacher teacher6 = new Teacher("22", 9, "信息系统分析与设计", "软件工程");
teacherHMap.put(teacher6, teacher1);
teacherHMap.put(teacher2, teacher2);
teacherHMap.put(teacher3, teacher3);
teacherHMap.put(teacher4, teacher4);
teacherHMap.put(teacher5, teacher5);
/*为什么是这个打印顺序?
Map根据键的HashCode值存储数据,
*/
System.out.println("-------------------往集合中添加5个“键-值”对象;对集合进行遍历,分别打印集合中的每个元素的键与值------------------------");
teacherHMap.forEach((k, v) -> {
System.out.println("键:" + k);
System.out.println("值:" + v);
System.out.println("-------------------------------------------");
});
/*获得集合中的所有键的集合,并进遍历*/
System.out.println("-------获得集合中的所有键的集合,并进遍历--------");
teacherHMap.keySet().forEach((key)-> {
System.out.println("键:" + key);
});
/*用entrySet获取键值对,并进行遍历*/
System.out.println("-------用entrySet获取键值对,并进行遍历11-------");
teacherHMap.entrySet().forEach((key)-> {
System.out.println("键值对:" + key);
});
System.out.println("-------练习使用Map.Entry,自己写一个测试111-------");
for(Map.Entry<Teacher,Teacher> e:teacherHMap.entrySet()){
Teacher key=e.getKey();
Teacher value=e.getValue();
System.out.println(key+":"+value);
}
System.out.println("-------练习使用Map.Entry,自己写一个测试222-------");
/* 练习使用Map.Entry,自己写一个测试*/
/*Iterator是一个接口,它是集合的迭代器。集合可以通过Iterator去遍历集合中的元素*/
Iterator<Map.Entry<Teacher, Teacher>> it=teacherHMap.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<Teacher,Teacher> entry=it.next();/*next():返回迭代器中的下一个元素*/
Teacher key=entry.getKey();
Teacher value=entry.getValue();
System.out.println(key+"\n"+value+"\n");
// Operator '+' cannot be applied to 'Teacher', 'Teache
//解决办法,值中间加个”“
}
}
}
【 实验结果】
TeacherTest1.java(List)
TeacherTest2.java(Map)