Java重写对象的 equals 方法和 hashCode 方法

在工作中,我们可能会遇到判断某一个对象是不是相等,而条件则是根据对象的几个字段的值来决定的。例如User对象,我们需要根据对象的userName判断两个对象是不是相等。
User.java


public class User {

	private Integer id;
	private String userName;
	private Integer age;
	private String desc;
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getDesc() {
		return desc;
	}
	public void setDesc(String desc) {
		this.desc = desc;
	}
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	
	public User(Integer id, String userName, Integer age, String desc) {
		super();
		this.id = id;
		this.userName = userName;
		this.age = age;
		this.desc = desc;
	}
	
	public User() {
		super();
	}
	
}

解决方案:
一、最直接也是最蠢的办法,逐个比较对象的字段值是否相等,我们可以直接抛弃这种。

二、重写对象的equals的方法


	@Override
	public boolean equals(Object obj) {
		//非空性:对于任意非空引用x,x.equals(null)应该返回false。
		if(obj == null){
			return false;
		}
		if(this == obj){//对象地址相等
			return true;
		}
		if(obj instanceof User){
			User user = (User)obj;
			return fieldCheck(this.userName,user.userName);
		}
		return false;
	}
	
	private boolean fieldCheck(String param1,String param2){
		//认为null值和""一样
		if(StringUtils.isEmpty(param1) && StringUtils.isEmpty(param2)){
			return true;
		}
		//非空值,两个值一样或者其中一个是空值
		return !StringUtils.isEmpty(param1) && param1.equals(param2);
	}

重写equals方法还需要重写hashCode方法

	@Override
	public int hashCode() {
		return userName.hashCode();
	}

上面只是一种简单的写法,当然为了提高运算效率,我们可以参考String的写法,改成下面的方式

@Override
	public int hashCode() {
		int result = 17;
        return 31 * result + (StringUtils.isEmpty(userName)?0:userName.hashCode());
	}

String源码:

/**
     * Returns a hash code for this string. The hash code for a
     * {@code String} object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using {@code int} arithmetic, where {@code s[i]} is the
     * <i>i</i>th character of the string, {@code n} is the length of
     * the string, and {@code ^} indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
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 方法返回是 0。并且注释中也给了个公式,可以了解了解。
 String 源码中也使用的 31,然后网上说有这两点原因:

原因一:更少的乘积结果冲突

  31是质子数中一个“不大不小”的存在,如果你使用的是一个如2的较小质数,那么得出的乘积会在一个很小的范围,很容易造成哈希值的冲突。而如果选择一个100以上的质数,得出的哈希值会超出int的最大范围,这两种都不合适。而如果对超过 50,000 个英文单词(由两个不同版本的 Unix 字典合并而成)进行 hash code 运算,并使用常数 31, 33, 37, 39 和 41 作为乘子,每个常数算出的哈希值冲突数都小于7个(国外大神做的测试),那么这几个数就被作为生成hashCode值得备选乘数了。

  所以从 31,33,37,39 等中间选择了 31 的原因看原因二。

原因二:31 可以被 JVM 优化

  JVM里最有效的计算方式就是进行位运算了:

  * 左移 << : 左边的最高位丢弃,右边补全0(把 << 左边的数据*2的移动次幂)。
  * 右移 >> : 把>>左边的数据/2的移动次幂。
  * 无符号右移 >>> : 无论最高位是0还是1,左边补齐0。   

       所以 : 31 * i = (i << 5) - i(左边  31*2=62,右边   2*2^5-2=62) - 两边相等,JVM就可以高效的进行计算啦。。。

好啦,说了这么多,下面来运行一下结果:

public class Test {

	public static void main(String[] args) {
		User u1 = new User(101,"W006733",23,"哈哈56哈哈-56");
		User u2 = new User(102,"W006733",33,"哈哈56哈哈-56");
		System.out.println(u1.equals(u2));
	}
}
	

不重写equals和hashCode方法,运行结果:
u1==u2------->false
重写equals和hashCode方法,运行结果:
u1==u2------->true
完美的实现了我们想要的结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值