HashSet
(1)特点:
存储顺序与元素添加的顺序无关;
非同步的,如果多个线程要操作HashSet,要通过代码来保持同步。
集合元素值可以是null
(2)HashSet添加元素的过程:
首先调用hashCode()方法来得到该对象的hashCode值,然后根据该hashCode值决定对象在HashSet中的存储位置。
如果该位置上没有元素,则直接存入;
如果该位置上已经保存了元素,调用equals方法,判断这两个元素是否相同。
如果返回true,认为相同,不保存;
如果不相同,保存。此时会在这个存储位置采用链式存储结构,将后来的元素保存在这个位置。
结果为:
F:\163netdisk\JAVA学习\java se>java Demo
调用hashCode计算Hashcode值,得到存储位置
调用hashCode计算Hashcode值,得到存储位置
调用equals判断与本存储位置的已有对象是否相同
2
调用hashCode计算Hashcode值,得到存储位置
Person@1
调用hashCode计算Hashcode值,得到存储位置
Person@1
可以看到先调用调用hashCode计算Hashcode值,得到存储位置。第二次存入时,由于hashcode均返回1,即存储位置相同,第二个元素计算得到的存储位置与第一个相同,此时需要判断这两个是否相同。equals均返回false,将两个元素都存储进来。
使用toString()输出hashCode可以看到两个元素的hashCode相同。
(3)hashCode复写的基本规则“
用作equals判断的成员变量都要用来计算hashcode。
equals相等的两个元素hashcode应该也相等。
重写的一般方式如下:
(a)把对象中每个有意义的成员(equals中使用的)计算出一个int行的hashCode值。
不同类型的Field计算HashCode的方式如下
boolean f hashcode=f?0:1;
byte/short/char/int f hashcode=(int)f
long f hashcode=(int)(f^(f>>>32))
float f hashCode=Float.floatToIntBits(f);
double f long lg = Double.doubleToLongBits(f);
hashCode=(int)(lg^(lg>>>32));
普通引用类型 f hashCode=f.hashCode();
(b)将a中计算出来的个Field的值组合计算出一个hashcode值返回。为了避免本来不相等,相加后相等的情况,可以给各Field乘以任意一个质数后再相加。
例如:hashCode=13*f1.hashCode()+17*(int)f2
(4)采用HashCode的优点
相当于以类似于数组的方式给每个元素添加了索引,通过索引可以快速的找到存储位置,得到元素。而不用一个一个的比较元素。HashSet为每个元素通过HashCode方法设置相应的HashCode值,HashCode值对应存储位置。这就是HashSet速度很快的原因。
(1)特点:
存储顺序与元素添加的顺序无关;
非同步的,如果多个线程要操作HashSet,要通过代码来保持同步。
集合元素值可以是null
(2)HashSet添加元素的过程:
首先调用hashCode()方法来得到该对象的hashCode值,然后根据该hashCode值决定对象在HashSet中的存储位置。
如果该位置上没有元素,则直接存入;
如果该位置上已经保存了元素,调用equals方法,判断这两个元素是否相同。
如果返回true,认为相同,不保存;
如果不相同,保存。此时会在这个存储位置采用链式存储结构,将后来的元素保存在这个位置。
可以看到存储首先判断的是hashCode值,即使两个元素相同(equals),只要他们的hashcode不同,也能被保存进来,显然这是不符合set的定义的。
实际我们比较对象一般使用equals,当equals返回的结果为相等时,认为两个对象相同。此时HashSet的hashCode也应该相等,Treeset使用的comparator的结果也应该为相等。即避免存入相同的元素。因此,将一个对象方法放到HashSet中时,如果重写了equals,也要重写hashcode方法,保证两者的一致性。
import java.util.*;
class Demo
{
public static void main(String[] args)
{
HashSet h = new HashSet();
h.add(new Person(1,13));
h.add(new Person(2,23));
System.out.println(h.size());
for(Object temp : h)
{
System.out.println(temp.toString());
}
}
}
class Person
{
private int id;
private int age;
public Person(int id, int age)
{
this.id = id;
this.age = age;
}
public boolean equals(Object obj)
{
System.out.println("调用equals判断与本存储位置的已有对象是否相同");
return false;
}
public int hashCode()
{
System.out.println("调用hashCode计算Hashcode值,得到存储位置");
return 1;
}
}
结果为:
F:\163netdisk\JAVA学习\java se>java Demo
调用hashCode计算Hashcode值,得到存储位置
调用hashCode计算Hashcode值,得到存储位置
调用equals判断与本存储位置的已有对象是否相同
2
调用hashCode计算Hashcode值,得到存储位置
Person@1
调用hashCode计算Hashcode值,得到存储位置
Person@1
可以看到先调用调用hashCode计算Hashcode值,得到存储位置。第二次存入时,由于hashcode均返回1,即存储位置相同,第二个元素计算得到的存储位置与第一个相同,此时需要判断这两个是否相同。equals均返回false,将两个元素都存储进来。
使用toString()输出hashCode可以看到两个元素的hashCode相同。
(3)hashCode复写的基本规则“
用作equals判断的成员变量都要用来计算hashcode。
equals相等的两个元素hashcode应该也相等。
重写的一般方式如下:
(a)把对象中每个有意义的成员(equals中使用的)计算出一个int行的hashCode值。
不同类型的Field计算HashCode的方式如下
boolean f hashcode=f?0:1;
byte/short/char/int f hashcode=(int)f
long f hashcode=(int)(f^(f>>>32))
float f hashCode=Float.floatToIntBits(f);
double f long lg = Double.doubleToLongBits(f);
hashCode=(int)(lg^(lg>>>32));
普通引用类型 f hashCode=f.hashCode();
(b)将a中计算出来的个Field的值组合计算出一个hashcode值返回。为了避免本来不相等,相加后相等的情况,可以给各Field乘以任意一个质数后再相加。
例如:hashCode=13*f1.hashCode()+17*(int)f2
(4)采用HashCode的优点
相当于以类似于数组的方式给每个元素添加了索引,通过索引可以快速的找到存储位置,得到元素。而不用一个一个的比较元素。HashSet为每个元素通过HashCode方法设置相应的HashCode值,HashCode值对应存储位置。这就是HashSet速度很快的原因。
================================================================
原创文章,转载请注明链接