java中重写equals和hashCode方法

时间:2023-01-05 16:09:01

为什么要重写equals和hashCode方法

1.需要将对象放入HsahMap、HashSet等集合中的类需要重写HashCode和equals()方法:

Hashcode在基于key-value的集合如:HashMap、LinkedHashMap中扮演很重要的角色。此外在HashSet集合中也会运用到,使用合适的hashcode方法在检索操作时的时间复杂度最好的是 O(1).

hashCode()和equals()定义在Object类中,这个类是所有java类的基类,所以所有的java类都继承这两个方法。

hashCode()方法被用来获取给定对象的唯一整数。这个整数被用来确定对象被存储在HashTable类似的结构中的位置。默认的,Object类的hashCode()方法返回这个对象存储的内存地址的编号。

参考Josh Bloch的书《Effective Java》,一个好的hashcode方法通常最好是不相等的对象产生不相等的hash值,理想情况下,hashcode方法应该把集合中不相等的实例均匀分布到所有可能的hash值上面。

补充一个类似的:什么时候类需要实现Serializable接口:当某个类的实体对象需要持久化到硬盘,或通过网络传输数据

2. 对于对象中的每个域,hashCode值遵循以下原则

为该域计算int类型的哈希值hc:

  • 如果是boolean类型,计算(f?1:0)
  • 如果是byte、char、short或者int类型,计算(int)f
  • 如果是long类型,计算(int)(f^(f>>>32))
  • 如果是float类型,计算Float.floatToIntBits(f)
  • 如果是double类型,计算Double.doubleToLongBits(f),然后重复第三个步骤。
  • 如果是一个对象引用,并且该类的equals方法通过递归调用equals方法来比较这个域,同样为这个域递归的调用hashCode,如果这个域为null,则返回0。
  • 如果是数组,要把每一个元素当作单独的域来处理,递归的运用上述规则,如果数组域中的每个元素都很重要,那么可以使用Arrays.hashCode方法。

把上面计算得到的hash值hc合并到result中

result = result*31 + hc;

String中的Hashcode方法
String的hashcode的算法就充分利用了字符串内部字符数组的所有字符。生成hash码的算法的在string类中看起来像如下所示,注意“s“是那个字符数组,n是字符串的长度。

s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

eclipse中自动生成Hashcode方法
如使用username 和password字段来生成hashCode

/**
* 根据userName和password来计算hashCode
*/

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((password == null) ? 0 : password.hashCode());
result = prime * result + ((userName == null) ? 0 : userName.hashCode());
return result;
}

企业级应用中一般推荐使用第三方库如Apache commons来生成hashocde方法。

Apache commons HashcodeBuilder
我们可以用Apache Commons hashcode builder来生成代码,使用这样一个第三方库的优势是可以反复验证尝试代码。下面代码显示了如何使用Apache Commons hash code 为一个自定义类构建生成hash code 。

public int hashCode(){
HashCodeBuilder builder = new HashCodeBuilder();
builder.append(mostSignificantMemberVariable);
.......................
builder.append(leastSignificantMemberVariable);
return builder.toHashCode();
}

equals方法重写

eclipse中自动生成equals方法
如使用username 和password字段来生成equals

    /**
* 重写equals()方法,只要name、pass相等的员工即可认为相等
*/

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
if (userName == null) {
if (other.userName != null)
return false;
} else if (!userName.equals(other.userName))
return false;
return true;
}

注意以下几点

  • 尽量保证使用对象的同一个属性来生成hashCode()和equals()两个方法。在我们的案例中,我们使用员工id。
  • eqauls方法必须保证一致(如果对象没有被修改,equals应该返回相同的值)
  • 任何时候只要a.equals(b),那么a.hashCode()必须和b.hashCode()相等。
  • 两者必须同时重写。

当使用ORM的时候特别要注意的

  • 如果你使用ORM处理一些对象的话,你要确保在hashCode()和equals()对象中使用getter和setter而不是直接引用成员变量。因为在ORM中有的时候成员变量会被延时加载,这些变量只有当getter方法被调用的时候才真正可用。
  • 例如在我们的例子中,如果我们使用e1.id == e2.id则可能会出现这个问题,但是我们使用e1.getId() ==
    e2.getId()就不会出现这个问题。