equals和hashCode是我们日常开发最常使用的方法,但是因为一般都使用默认的规则,因此也很少会引起关注。不过了解他们的用途和设计的原则,还是会帮助我们更好的设计代码。

equals

equals是java很基础的一个问题,通常都会跟==来做比较。那么看看下面的问题:

int a = 1;

int b = 1;

System.out.println(a==b);//true

Integer a1 = new Integer(1);

Integer a2 = new Integer(1);

System.out.println(a1==a2);//false

System.out.println(a1.equals(a2));//true

这是因为,==比较的是引用,而equals比较的内容,进入到Integer的源码中,就可以看到它其实重写了equals方法:

public boolean equals(Object obj) {

if (obj instanceof Integer) {

return value == ((Integer)obj).intValue();

}

return false;

}

说到equals,就不得不说如果我们想要自己重写equasl都需要注意哪些地方。首先就是equals方法的设计需要满足下面几个特性:

自反性:对于所有的非null,a=a

对称性:a=b,则b=a

传递性:a=b,b=c,则a=c

一致性:对于没有被修改的ab,如果a=b,则一直a=b

非空性:对于任何非Null,a!=null

因此推荐最佳的equals设计方法应该是

@Override

public boolean equals(Object o){

if(o == this)

return true;

if(!(o instanceof XX))

return false;

XX xx = (XX)o;

return xx.x.equals(o.x);//TODO

}

另外,还需要注意的是,如果覆盖了equals方法,那么还需要注意它的hashCode.

hashCode

这个hashCode其实是有特定的使用场景的,比如List或者数组就不会用到hashCode,如果是HashMap、HashSet、HashTale,那么hashCode就十分重要了。

这是因为在类似HashSet的集合中,是需要对元素去重,那么如何判断两个元素是相同的呢?如果每次调用equals,那么代价有点大,这是因为如果对象内部的属性很多,equals一般需要对比每一项。所以就是用hashCode作为对比的方法。

下面看看Hashtable中添加一个元素的逻辑:

public synchronized V put(K key, V value) {

// 检查value是否为空

if (value == null) {

throw new NullPointerException();

}

// 确保key没有在table中

Entry tab[] = table;

int hash = key.hashCode();//获取key的hash值

// 通过对table的length取余,确定对应的存储位置

int index = (hash & 0x7FFFFFFF) % tab.length;

// 挨个对比是否相同

Entry entry = (Entry)tab[index];

for(; entry != null ; entry = entry.next) {

if ((entry.hash == hash) && entry.key.equals(key)) {

V old = entry.value;

entry.value = value;

return old;

}

}

addEntry(hash, key, value, index);

return null;

}

在HashMap和HashSet中比较类似,都是下面的逻辑:

public V put(K key, V value) {

return putVal(hash(key), key, value, false, true);

}

static final int hash(Object key) {

int h;

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

可以看到它们都使用了HashCode,只不过是方式方法不一样而已。

在不同的对象中hashCode的生成规则也是不一样的,不过最终的目的都是返回一个int值。比如,在Integer中:

public static int hashCode(int value) {

return value;

}

在String中:

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;

}

因此设计hashCode,需要遵循下面的原则:

在程序期间,同一个对象调用hashCode,返回的是一个数

如果x.equals(y)返回true,那么它们的hashCode肯定也相等

如果x.equals(y)返回false,那么它们的hashCode有可能相等,也有可能不相等。

换句话说,判断两个对象是否相等:

如果他们的hashCode不相等,那么它们肯定不想等;如果相同,还得对比equals

如果两个对象的equals方法不同,那么对象就不同;否则,对象相同

查看原文