Java对象的等价

在Java中通常使用equals方法和hashCode方法来直接地或间接地判断两个对象是否等价,并且在Java的一些库中有可能直接调用这些函数来判断等价性,例如在HashSet和HashMap中就通过调用hashCode方法来获得一个对象的哈希值,从而区分对象和维护数据结构.然而Java为对象设置的默认equals方法和hashCode方法都是基于对象地址的,所以我们有必要根据类的各个域和实际需求重新设计equals方法和hashCode方法.根据实现的不同,我们分可变类和不可变类讨论.

不可变类

equals方法

equals方法,和其他方法相同,继承自一个类的父类,那么当我们从头开始构建一个类的时候,equals方法就继承自Object类,如下:

public boolean equals(Object obj) {
        return (this == obj);
}

可以看到返回的是this == obj的结果,==在对对象应用是比较的是对象地址,这一般不是我们想要的,我们一般需要通过对象的某些内容来区分等价与否,例如对定义如下的Employee类,我们希望通过区分员工的名字和id来区分不同的员工.

public class Employee {
    final String name;
    final String ID;
    final String position;
    final String phone;
    public Employee(String name, String ID String position, String phone){
        this.name = name;
        this.ID = ID;
        this.position = position;
        this.phone = phone;
    }

    //Other Methods...
}

那么我们就可以如下地重写equals方法.

@Override
public boolean equals(Object o) {
    if(!(o instanceof Employee))
        return false;
    Employee ano  = (Employee)o;
    return (this.name.equals(ano.name) && this.ID.equals(ano.ID));
}

值得注意的是,我们在对比name和ID之前首先判断参数是不是一个Employee对象,如果不是,那么更不用说等价的问题了.

hashCode方法

Java中对hashCode方法的一般要求是,对一个对象每次调用时返回相同的int类型数,并且当两个对象使用equals方法判断等价时时返回的值相等.默认的,也就是Object类的hashCode方法相当复杂,它根据对象的地址和Java内存机制完成了上述的要求,但这通常也不是我们想要的,我们还是需要重写hashCode方法来改变等价的判断方法.

@Override
public int hashCode() {
    int res = name.hashCode();
    res = res*31+ID.hashCode();
    return res;
}

和equals方法相同,我们使用了两个关键域的hashCode值的运算来保证我们认为等价的两个对象hashCode值相等.同时我们进行这个乘31并相加的运算,是为了让不等价的对象的hashCode值更趋于不同,虽然这不是必要的,但这一点能够提升HashMap等类的性能.

对于一个不可变类,我们总是需要重写equals方法和hashCode方法来重新定义这个类的等价性,否则在使用一些数据结构(List,HashMap等)时,很有可能遇到一些令人迷惑的错误.

可变类

直接或间接地使用不可变类的hashCode方法并不安全,举例来说,下面的HashSet<ArrayList<String>>展现出了很令人迷惑的行为

HashSet<ArrayList<String>> listSet = new HashSet<>();
ArrayList<String> l1 = new ArrayList<>();
l1.add("test");
listSet.add(l1);
System.out.println(listSet.contains(l1));
l1.add("test2");
System.out.println(listSet.contains(l1));

我们可以查看ArrayList的hashCode方法,发现它的返回值是受每个元素影响的.

也就是说,在第二次向l1中插入串后,l1的hashCode值就改变了,再次调用listSet.contains方法时,使用l1新的hashCode值去寻找,结果就是什么都找不到.

根据上面的例子,我们在使用可变类时最好不要直接或间接地使用它的hashCode值,对我们自己定义的可变类来说,也就没有必要特别设计它的hashCode和equals方法了.在需要对比可变类时,可以使用更复杂而更具体的方法.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值