案例:宠物猫信息管理(HashSet增删改查)
需求分析
1. 功能需求
(1) 添加和显示宠物猫信息
(2) 查找某只宠物猫的信息并输出
(3) 修改宠物猫的信息
(4) 删除宠物猫信息
2. 属性
(1) 名字name
(2) 年龄month
(3) 品种species
3. 方法
(1) 构造方法
(2) 获取和设置属性值的方法
(3) 其他方法
创建宠物猫Cat类,定义构造方法,定义get/set方法
public class Cat {
private String name; //名字
private int month; //年龄
private String species;//品种
//构造方法
public Cat(String name, int month, String species) {
super();
this.name = name;
this.month = month;
this.species = species;
}
//getter与setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
}
主方法内定义宠物猫对象,将宠物猫对象放入HashSet中,显示宠物猫信息。
// 定义宠物猫对象
Cat huahua = new Cat("花花", 12, "英国短毛猫");
Cat fanfan = new Cat("凡凡", 3, "中华田园猫");
// 将宠物猫对象放入HashSet中
Set set = new HashSet();
set.add(huahua);
set.add(fanfan);
// 显示宠物猫信息
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
重写toString方法
需要重写toString方法才能显示猫猫具体信息,否则显示的是猫猫的地址信息(这是因为这里的对象是Object类)。重写toString方法后,it.next()会自动调用toString方法。
可以通过Source
→
\to
→Generate toString()…来快速重写toString。
@Override
public String toString() {
return "[姓名:" + name + ", 年龄:" + month + ", 品种:" + species + "]";
}
再添加一个与花花属性一样的猫
// 再添加一个与花花属性一样的猫
Cat huahua01 = new Cat("花花", 12, "英国短毛猫");
set.add(huahua01);
System.out.println("**********************************");
System.out.println("添加重复数据后的宠物猫信息:");
it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
这里发现运行结果为:
**********************************
添加重复数据后的宠物猫信息:
[姓名:花花, 年龄:12, 品种:英国短毛猫]
[姓名:花花, 年龄:12, 品种:英国短毛猫]
[姓名:凡凡, 年龄:3, 品种:中华田园猫]
我们发现,这并不符合集合的规则:相同的字符串不允许添加到集合。
解决方法:重写hashCode()和equals()方法,来判断宠物信息是否相等。
hashCode()(哈希表)理解
例如,查找100以内的能被的数据。如果使用ArrayList进行查找,那么将会从头到尾遍历一遍,非常麻烦,而且费时间。使用hashCode()(哈希表)进行处理,哈希表会将100以内的数据分为3个桶,桶里分别装能被1整除的数据、能被2整除的数据和能被3整除的数据,首先会根据规则(即能被1、2或者3整除)判断需要查找的数据在哪个桶内,然后在那个桶内遍历桶里的元素,看是不是在桶内。
上述过程中hashCode()方法负责判断需要查找的数据在哪个桶里边,而equals()方法负责判断桶里哪一个才是我们要找的元素。
hashCode()和equals()大大提高了查询的效率,因此我们要对其进行重写。
重写hashCode()和equals()方法
快捷重写:SourceGenerate hashCode() and equals()
hashCode()内容自动重写,不用管,equals()方法我们得自己写一下。
重写equals()方法的步骤:
``java
@Override
public boolean equals(Object obj) {
//判断对象是否相等,相等则返回true,不用继续比较属性了
if(this==obj)
return true;
//判断obj是否是Cat类的对象
if(obj.getClass()==Cat.class){
Cat cat=(Cat)obj;
return cat.getName().equals(name)&&(cat.getMonth()==month)&&(cat.getSpecies().equals(species));
}
return false;
}
(1) 首先判断对象是否相等,如果对象相等则直接返回true,不用继续比较了。其中this是当前类的对象,obj是传入的对象;
(2) 然后判断obj所对应的对象是否为Cat,如果是Cat类型的对象,则强制转换为Cat类的对象。然后判断名字、年龄和品种是否相等;
重写equals()方法后,再添加一个与花花属性一样的猫后,集合就不会显示重复的内容,也就是添加不进去了,这就符合了Set中不能有重复数据的规则了。
# 查找宠物猫信息
```java
System.out.println("通过名字查找花花信息");
boolean flag = false;
Cat c = null;
it = set.iterator();
while (it.hasNext()) {
c = (Cat)it.next();
if (c.getName().equals("花花")) {
flag = true;// 找到了
break;
}
}
我们发现,如果将 c = (Cat)it.next();
修改为String c1 = (String)it.next();
后编译时报错,但是运行时会报错,显示异常:java.lang.ClassCastException,即类型转换异常,这是因为Cat类型不能转换为String类型,因此我们要加入泛型的概念,加入泛型后就无需进行强制转换了。
加入泛型
泛型设定了在Set(集合)中添加的数据必须是我们所需要的类型。
在CatTest类中修改:
// 将宠物猫对象放入HashSet中
Set<Cat> set = new HashSet<Cat>();
// 显示宠物猫信息
Iterator<Cat> it = set.iterator();
这样在查找信息时,无需进行强制转换,直接查找即可:
System.out.println("通过名字查找花花信息");
boolean flag = false;
Cat c = null;
it = set.iterator();
while (it.hasNext()) {
c = it.next();
if (c.getName().equals("花花")) {
flag = true;// 找到了
break;
}
}
注:这里集合和迭代器都加入泛型<Cat>
,这样就限制了集合和迭代器中的数据都是Cat类型的对象了。
删除宠物猫的信息
使用增强型for循环编写(将集合中的Cat类型的数据依次取出)。
// 删除花花二代的信息并重新输出
for (Cat cat : set) {
if ("花花二代".equals(cat.getName())) {
set.remove(cat);
break; }
}
System.out.println("**********************************");
System.out.println("删除花花二代后的数据");
for(Cat cat:set){
System.out.println(cat);
}
删除集合中的所有宠物猫信息
//删除集合中的所有宠物猫信息
System.out.println("**********************************");
boolean flag1=set.removeAll(set);
if(flag1){
System.out.println("猫都不见了。。。");
}else{
System.out.println("猫还在。。。");
}
注:这里 flag1
换成 set.isEmpty()
也可以。
删除所有年龄小于5个月的小猫
步骤:先建立子集合再删除这个子集合。如果这里不建立子集合直接删除,会导致异常:java.util.ConcurrentModificationException,这是因为不能删除正在读取中的数据。
// 删除年龄为五个月内的小猫集合
Set<Cat> set1 = new HashSet<Cat>();
for(Cat cat:set) {
if(cat.getMonth()<5) {
set1.add(cat);
}
}
set.removeAll(set1);
System.out.println("**********************************");
hashCode和equals方法的作用
hashCode()方法用于给对象返回hash code值,equals方法用于判断其他对象与该对象是否相等。为什么需要这两个方法呢?我们知道HashSet中是不允许添加重复元素的,那么当调用add()方法向HashSet中添加元素时,是如何判断两个元素是不同的呢?这就用到了hashCode()和equals()方法。在添加数据时,会调用hashCode()方法得到hash code值,通过这个值可以找到数据存储位置,该位置可以理解成一片区域,在该区域存储的数据的hashCode值都是相等的。如果该区域已经有数据了,就继续调用equals()方法判断数据是否相等,如果相等就说明数据重复了,就不能再添加了。如果不相等,就找到一个位置进行存储。
这些是基于哈希算法完成的,它使得添加数据的效率得到了提升。假设此时Set集合中已经有100个元素,那么如果想添加第101个元素,如果此时没有使用哈希算法,就需要调用equals()方法将第101个元素与之前100个元素依次进行比较,如果元素更多,比较所耗费的时间就越长。
如果两个对象相等,那么他们的hashCode值一定相等。反之,如果两个对象的hashCode值相等,那么这两个对象不一定相等,还需要使用equals()方法进行判断。
如果不重写hashCode()方法,则默认每个对象的hashCode()值都不一样,所以该类的每个对象都不会相等。
关于getClass()和.class的作用
案例:
@Override
public boolean equals(Object obj){
if(this==obj)
return true;
if(obj.getClass()==Cat.class)
}
以上代码中的第二个if语句,其中的判断条件中用到了getClass()和.class。
首先,来看一下Class类。在Java中,万事万物皆对象,每个类都有一个相应的Class对象。通过Class类,可以获得一个类的基本信息,比如属性、方法和构造方法等。这些都属于Java反射的内容,在后面的课程中将会学习到。
getClass()是Object类的方法,该方法的返回值类型是Class类,通过getClass()方法可以得到一个Class类的对象。而.class返回的也是Class类型的兑现。所以,如果obj.getClass()和Cat.class返回的内容相等,说明是同一个对象。
既然都可以得到Class的对象,关于getClass()和.class的区别:getClass()方法,有多态能力,运行时可以返回子类的类型信息。.class是没有多态的,是静态解析的,编译时可以确定类型信息。
宠物猫信息管理完整代码
Cat类
package com.study.set;
public class Cat {
private String name; //名字
private int month; //年龄
private String species;//品种
//构造方法
public Cat(String name, int month, String species) {
super();
this.name = name;
this.month = month;
this.species = species;
}
//getter与setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public String getSpecies() {
return species;
}
public void setSpecies(String species) {
this.species = species;
}
@Override
public String toString() {
return "[姓名:" + name + ", 年龄:" + month + ", 品种:" + species + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + month;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((species == null) ? 0 : species.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
//判断对象是否相等,相等则返回true,不用继续比较属性了
if(this==obj)
return true;
//判断obj是否是Cat类的对象
if(obj.getClass()==Cat.class){
Cat cat=(Cat)obj;
return cat.getName().equals(name)&&(cat.getMonth()==month)&&(cat.getSpecies().equals(species));
}
return false;
}
}
CatTest类
package com.study.set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class CatTest {
public static void main(String[] args) {
// 定义宠物猫对象
Cat huahua = new Cat("花花", 12, "英国短毛猫");
Cat fanfan = new Cat("凡凡", 3, "中华田园猫");
// 将宠物猫对象放入HashSet中
Set<Cat> set = new HashSet<Cat>();
set.add(huahua);
set.add(fanfan);
// 显示宠物猫信息
Iterator<Cat> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 再添加一个与花花属性一样的猫
Cat huahua01 = new Cat("花花", 12, "英国短毛猫");
set.add(huahua01);
System.out.println("**********************************");
System.out.println("添加重复数据后的宠物猫信息:");
it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println("**********************************");
// 重新插入一个新宠物猫
Cat huahua02 = new Cat("花花二代", 2, "英国短毛猫");
set.add(huahua02);
System.out.println("添加花花二代后的宠物猫信息:");
it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println("**********************************");
// 在集合中查找花花的信息并输出
if (set.contains(huahua)) {
System.out.println("花花找到了!");
System.out.println(huahua);
} else {
System.out.println("花花没找到!");
}
// 在集合中使用名字查找花花的信息
System.out.println("**********************************");
System.out.println("通过名字查找花花信息");
boolean flag = false;
Cat c = null;
it = set.iterator();
while (it.hasNext()) {
c = it.next();
if (c.getName().equals("花花")) {
flag = true;// 找到了
break;
}
}
if (flag) {
System.out.println("花花找到了");
System.out.println(c);
} else {
System.out.println("花花没找到");
}
// 删除花花二代的信息并重新输出
for (Cat cat : set) {
if ("花花二代".equals(cat.getName())) {
set.remove(cat);
break; }
}
System.out.println("**********************************");
// 删除年龄为五个月内的小猫集合
Set<Cat> set1 = new HashSet<Cat>();
for(Cat cat:set) {
if(cat.getMonth()<5) {
set1.add(cat);
}
}
set.removeAll(set1);
System.out.println("**********************************");
System.out.println("删除花花二代后的数据");
for(Cat cat:set){
System.out.println(cat);
}
//删除集合中的所有宠物猫信息
System.out.println("**********************************");
boolean flag1=set.removeAll(set);
if(set.isEmpty()){
System.out.println("猫都不见了。。。");
}else{
System.out.println("猫还在。。。");
}
}
}
运行结果
[姓名:花花, 年龄:12, 品种:英国短毛猫]
[姓名:凡凡, 年龄:3, 品种:中华田园猫]
**********************************
添加重复数据后的宠物猫信息:
[姓名:花花, 年龄:12, 品种:英国短毛猫]
[姓名:凡凡, 年龄:3, 品种:中华田园猫]
**********************************
添加花花二代后的宠物猫信息:
[姓名:花花, 年龄:12, 品种:英国短毛猫]
[姓名:凡凡, 年龄:3, 品种:中华田园猫]
[姓名:花花二代, 年龄:2, 品种:英国短毛猫]
**********************************
花花找到了!
[姓名:花花, 年龄:12, 品种:英国短毛猫]
**********************************
通过名字查找花花信息
花花找到了
[姓名:花花, 年龄:12, 品种:英国短毛猫]
**********************************
**********************************
删除花花二代后的数据
[姓名:花花, 年龄:12, 品种:英国短毛猫]
**********************************
猫都不见了。。。