开发过程中,当自己创建的类没有重写Object的hashCode()方法时,那么对象在散列集合中的操作,将默认使用Object的hash算法,即与对象内存地址有关的本地方法返回的数值,这样导致一些实例域相同的对象最终比较结果却是不同的。
那么,如果想要实现自己的hashCode方法,如何实现比较好呢。
在方法中可以使用Objects.hashCode()方法,来生成域的散列码,将各个域的散列码通过与素数相乘再通过自己定义的运算组成产生新的哈希码,如(((属性one哈希码 * 31)+属性two哈希码) * 31)以此类推,将生成一个依赖属性1和属性2的顺序和值的哈希码,至于为啥是31,好像是根据大神科学家们的统计使用31出现相同的哈希冲突频率最低。反之如下定义hashcode则两个属性互换值也将产生相同的哈希值。
public class ObjectTest {
private String name;
private String local;
public ObjectTest(String name,String local) {
this.name=name;
this.local=local;
}
public static void main(String[] args) {
System.out.println(new ObjectTest("a","b").hashCode()==new ObjectTest("b","a").hashCode());
}
public int hashCode() {
return name.hashCode()*31+local.hashCode()*31;
}
}
或者hash(Object… values)更为方便。
hash()方法:
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
那么element的hashCode方法返回的是什么呢?
常见的String类型的hashCode方法:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
如果自己定义的类中包含基本类型的属性,那么element.hashCode()是什么呢?
其实会调用包装类的hashCode方法
Integer:
private final int value;
@Override
public int hashCode() {
return Integer.hashCode(value);
}
public static int hashCode(int value) {
return value;
}
Double:
@Override
public int hashCode() {
return Double.hashCode(value);
}
public static int hashCode(double value) {
long bits = doubleToLongBits(value);
return (int)(bits ^ (bits >>> 32));
}
Character:
@Override
public int hashCode() {
return Character.hashCode(value);
}
public static int hashCode(char value) {
return (int)value;
}
Boolean:
@Override
public int hashCode() {
return Boolean.hashCode(value);
}
public static int hashCode(boolean value) {
return value ? 1231 : 1237;
}
另外,如果自己定义的hashCode返回的哈希值依赖于一些容易变化的属性,那么往HashSet中add一个对象后,对象属性改变,则无法通过Set接口的contains方法确定是否集合中存在该对象。代码如下:
public class CollectionTest {
private String field;
public CollectionTest(String field) {
this.field=field;
}
@Override
public int hashCode() {
return field.hashCode();
}
public void setField(String field) {
this.field=field;
}
@Override
public String toString() {
return field;
}
public static void main(String args[]) {
//Set
Set<CollectionTest> set = new HashSet<>();
CollectionTest test = new CollectionTest("a");
set.add(test);
test.setField("b");
System.out.println(set.contains(test));
set.add(test);
System.out.println(set.toString());
}
}
结果如下:
false
[b, b]