注:重要笔记在代码中注释有
hashSet去重:
即判断两个对象是否相等
1:会先调用对象的hashCode方法获得hash的值,如果set中哈希表里面没有对应的hash值,则将次对象存入set中
2:如果set中hash表里面有对应的hash值,就让后面的对象调用equals方法和之前的hash值不同的对象进行比较,如果返回为true就证明存在,不在储存,入伙返回为false则视为新对象,加入到set中。
对于hashset不可重复,如何判断对新加入的对象是否重复
public int hashCode() {
// TODO Auto-generated method stub
// 1:int 整数
// 2:为了区分不相同 一般使用素数 不易产生碰撞
// 3:与属性值有一定的关联
int prim = 31;
int resultCode = prim*(prim+1) + (brand==null?0:brand.hashCode());
return resultCode;
}
// equals方法是object的方法
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
// 内存地址 引用传递是否相等
if (this == obj) {
return true;
}
// 空判断
if (obj == null) {
return false;
}
// 判断是否同属于一个类
if (getClass() != obj.getClass()) {
return false;
}
Phone myPhone =(Phone)obj;
// 判断属性 brand price
return this.brand.equals(myPhone.brand)&&this.price==myPhone.price;
}
Java编程使用HashSet添加对象时,由于要符合Set的特点(没顺序,不重复)所以必须重写equals方法和hashCode方法。为什么要这样呢?请看:Java中关于HashSet添加自定义对象时,自定义类要重写equals方法和hashCode方法的前世今生...
第一:Set集合没有顺序,也不允许重复。为什么要这样:模拟现实的集合。这里的重复只是:对象的重复何为对象的重复:指的就是同一个对象。何为同一个对象:内存中,所在的内存编号一致。内存编号的表示是什么:哈希码(哈希码一般是 类名 和 对象所在内存地址的十六进制数字表示 的组合)。
第二:这种设置和实现中的矛盾在什么地方:现实生活中只要属性相同,我们就认为那是同一个对象。这与计算机比较同一个对象的方法不同(计算机使用内存地址,即哈希码)于是,就需要重写equals方法和hashCode方法(&&)来让程序的运行结果符合现实生活基本数据类型的实现类都已经重写了上面的两个方法。
第三:为什么要重写equals方法和hashCode方法(技术实现原理):程序向HashSet中添加一个对象时,先用hashCode方法计算出该对象的哈希码。比较: (1),如果该对象哈希码与集合已存在对象的哈希码不一致,则该对象没有与其他对象重复,添加到集合中!(2),如果存在于该对象相同的哈希码,那么通过equals方法判断两个哈希码相同的对象是否为同一对象(判断的标准是:属性是否相同) 1>,相同对象,不添加。 2>,不同对象,添加!
这时有两个疑问:1,为什么哈希码相同了还有可能是不同对象?2,为什么经过比较哈希码还需要借助equals方法判断?
答:首先:按照Object类的hashCode方法,是不可能返回两个相同的哈希码的。(哈希码唯一标志了对象) 然后:Object类的hashCode方法返回的哈希码具有唯一性(地址唯一性),但是这样不能让程序的运行逻辑符合现实生活。(这个逻辑就是:属性相同的对象被看作同一个对象。)为了让程序的运行逻辑符合现实生活,Object的子类重写了hashCode的方法(基本数据类型的实现类都已经重写了两个方法,自定义的类要软件工程 师自己重写。)
那么:重写的宗旨是什么?重写就是为了实现这样的目的:属性相同的不同对象在调用其hashCode方法后,返回的是同样的哈希码。但是我们在重写的时候,发现几乎所有的写法都无法避免一个bug:有一些属性不同的对象(当然是不同的对象),会返回相同的哈希码。(即 重码)
最后:为了解决这个问题:在哈希码相同的时候,再用equals方法比较两个对象的对应属性是否相同,这样,确保了万无一失。这样:上面两个问题得到解决。
/**
*
*/
package com.zhiyou.R;
import java.util.HashSet;
import java.util.Set;
/**
* @author Administrator
*
*/
public class HashTest {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// set中去重,依据什么?
// 去重依据的并不是内存地址,依据的是哈希值。
// 哈希值就是一个int类型的整数
String string1 = new String("laosun");
String string2 = new String("laosun");
System.out.println(string1.hashCode());
System.out.println(string2.hashCode());
String string3 = new String("Aa");
String string4 = new String("BB");
System.out.println(string3.hashCode());
System.out.println(string4.hashCode());
// 哈希值任何对象都存在的,因为hashCode()是object的方法
// hash值不相同 ,两个元数据一定不相等
// hash值相同,两个元数据不一定相等
// hash值犹如一个人的人名
// 两个人名字不相同,肯定是不同的两个人
// 名字相同,不一定指的是同一个人,可能重名
// set中去重,指的是先判断已经存在的对象的hash值,如果新加入的对象的
// hash值和之前的的不一样,那么加入的就是一个新的对象,但是如果相等
// ,还仍需要判断两个对象是否相等(重写equals方法)判断对象中属性值
// 是否和之前的对象的属性相同,相同代表一样,不同代表新的对象
// 既然hashCode()是第一优先要获得的,但是hash值一样,也有可能原内容不相同
// 所以对于自定义类Phone,必须同时实现hashCode()和equals(0这两个方法。
// hashCode存在的意义是可以快速的判断出来与之前大量的对象不一样,
// 只要hash值不同代表就是一个新对象
aboutEqualsHashSet();
}
public static void aboutEqualsHashSet() {
Set<Phone> phone = new HashSet<>();
phone.add(new Phone("xiaomi",799));
phone.add(new Phone("xiaomi",799));
phone.add(new Phone("xiaomi",799));
phone.add(new Phone("xiaomi",799));
System.out.println(phone.size());
}
}
/**
*
*/
package com.zhiyou.R;
/**
* @author Administrator
*
*/
public class Phone {
private String brand;
private double price;
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public Phone(String brand, double price) {
super();
this.brand = brand;
this.price = price;
}
public Phone() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Phone [brand=" + brand + ", price=" + price + "]";
}
// 将来Phone对象调用下面的方法活儿对应的hash值
// 依据brand的hash值来确定对象的hash值
@Override
public int hashCode() {
// TODO Auto-generated method stub
// 1:int 整数
// 2:为了区分不相同 一般使用素数 不易产生碰撞
// 3:与属性值有一定的关联
int prim = 31;
int resultCode = prim * (prim + 1) + (brand == null ? 0 : brand.hashCode());
return resultCode;
}
// equals方法是object的方法
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
// 内存地址 引用传递是否相等
if (this == obj) {
return true;
}
// 空判断
if (obj == null) {
return false;
}
// 判断是否同属于一个类
if (getClass() != obj.getClass()) {
return false;
}
Phone myPhone = (Phone) obj;
// 判断属性 brand price 是否和myPhone的相等
// price
if (this.price != myPhone.price) {
return false;
}
// brand
if (this.brand == null) {
if (myPhone.brand == null) {
return false;
}
} else if (!this.brand.equals(myPhone.brand)) {
return false;
}
return true;
}
}