Set
set类似于一个罐子,程序可以把多个对象全部丢到罐子中,从表面上看,Set和Collection的用法差不多,但是Set有着自己的特点:
元素无序,不能添加重复元素
HashSet
1.简介:
HashSet是Set接口的典型实现,拥有着以下特点:
*不能保证元素的排列顺序,添加顺序和输出顺序可能不同
*HashSet不是同步的,如果多个线程同时访问一个HashSet,假设多个线程同时修改集合时,需要通过代码来进行同步
*集合元素值可能是null
2.使用事项
由于HashSet是不能添加重复元素的,如何判断元素是否相同呢?
HashSet会调用hashCode方法先计算出hashCode对象值从而知晓该元素在HashSet中的位置,同时调用equals方法(返回boolean)进行判断两个对象是否一致
下面先举一个栗子:
import java.util.HashSet;
public class Demo1 {
//测试HashSet的性质
//无序,不能出现重复的elem
/**
* 在HashSet中如何判断元素是否相同 1.HashCode一致 2.equals返回true
*/
//重写equals方法
static class A {
@Override
public boolean equals(Object obj) {
return true;
}
}
static class B {
@Override
public int hashCode() {
return 1;
}
}
static class C {
@Override
public int hashCode() {
return 2;
}
@Override
public boolean equals(Object obj) {
return true;
}
}
//测试添加类的情况
public static void main(String[] args) {
HashSet books = new HashSet();
books.add(new A());
books.add(new A());
books.add(new B());
books.add(new B());
books.add(new C());
books.add(new C());
System.out.println(books);
//
/**
*
}
}
总结一下:
结论:出现了两个A 两个B 一个C
*说明只有当equals和HashCode都相同的时候 才证明在HashSet中为相同的元素
*在HashSet中一旦遇见相同的元素,add()方法返回false 直接不加入(HashSet中无法加入相同的元素)
* HashSet规则:如果两个对象通过equals的方法比较返回true,则这两个对象的hashCode值也应该相同
* 当把对象加入HashSet中,如果需要重写该对象对应的类的equals()方法,则也应该重写其hashCode()方法
* 如果两个对象的equals相同,但是hashCode不同,所以两个对象会根据HashCode放在Hash表的不同位置,从而使得两个对象都可以添加成功,但是和规则相悖
* 如果两个对象的hashCode相同 但是.equals()方法返回false,所以只能使用一种链式结构来保存对象,所以当一个HashSet中两个以上的元素具有相同的hashCode值 会导致性能下降
3.具体说明:
上述为了证明hashSet的性质,重写了hashCode方法和equals方法,但是,其实重写这些方法也要遵循一定的原则
*程序运行时,同一个对象多次调用hashCode方法时应该返回相同的值
*当两个对象通过equals()方法返回true时,两个对象的hashCode()方法应该也返回相同的值
注:如何计算hashCode()?
1.单独的实例变量计算
2.然后将计算出的多个hashCode值进行组合计算出一个总的hashCode值
demo:(参考:https://www.cnblogs.com/zhshlimi/p/8471752.html)
@Override
public int hashCode() {
//设置初始值
int result = 17;
//假设有效域为: name,age,idCardNo,incomeAnnual,sex,brithDay
int c = 0;
//计算name (String为对象类型,他的计算直接调用本身的hashCode)
c = name.hashCode();
result = result * 37 + c;
//计算age (int/byte/char/short类型,他的计算直接调用本身的值)
c = this.getAge();
result = result * 37 + c;
//计算idCardNo (long类型,他的计算 (int)(field^(field >>> 32)) 无符号右移32位)
c = (int) (this.idCardNo ^ (this.idCardNo >>> 32));
result = result * 37 + c;
//计算 incomeAnnual (double类型,他的计算 Double.doubleToLongBits(field)后,再按Long类型计算 )
//(float类型,他的计算 Float.floatToIntBits(field) )
long tmp = Double.doubleToLongBits(this.incomeAnnual);
c = (int) (tmp ^ (tmp >>> 32));
result = result * 37 + c;
//计算 sex (sex为boolean类型,他的计算直接调用 c=sex?1:0)
c = this.isSex() ? 1 : 0;
result = result * 37 + c;
//计算 brithDay (brithDay为Date对象类型,他的计算直接调用 本身的hashCode)
c = this.getBirthDay().hashCode();
result = result * 37 + c;
return result;
}