Java中的equals()和hashCode()方法

    在Java语言中,一般不需要自定义equals()和hashCode()这两个方法,当需要对对象的内容进行比较的时候,才需要这样两个方法。例如,需要把对象放入HashSet或者把对象作为key放入HashMap、 Hashtable时,如果不想把具有相同内容的两个对象作为两个对象来看待,就需要重定义这两个方法。这两个方法的使用是紧密配合的,要是设计了其中一个,就要设计另外一个。

    equals():
    用于两个对象的比较,在Object类中已经实现了这个方法,是对对象内部地址的比较,即如果两个对象的内部地址是一样的则是相等的。如果要按照对象内容的进行比较,就需要重载这两个方法。Java语言对equals()的要求如下,这些要求是必须遵循的:
  • 对称性:如果x.equals(y)返回是“true”,那么y.equals(x)也应该返回是“true”。
  • 反射性:x.equals(x)必须返回是“true”。
  • 类推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也应该返回是“true”。
  • 一致性:如果x.equals(y)返回是“true”,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是“true”。
    任何情况下,x.equals(null)永远返回是“false”;x.equals(和x不同类型的对象)永远返回是“false”。
    hashCode():
    这个方法返回的是一个用来进行hash操作的整型数,可用于在Collection对象中定位特定对象的位置。Object中默认的实现是把对象内部地址转化为整数作为hashCode。
    hashCode()的返回值和equals()的关系如下:
  • 如果x.equals(y)返回“true”,那么x和y的hashCode()必须相等。
  • 如果x.equals(y)返回“false”,那么x和y的hashCode()有可能相等,也有可能不等。
    这里有必要说明一下HashMap的原理。为了优化查找对象的性能,在HashMap中按照键值对象的hash值放了若干个箱子,当有一个键值对象加入进来时,调用键值对象的hashCode()方法,根据计算出的hash值把对象放入对应的箱子。当对键值对象进行查找时,首先计算对象的hash值,找到对应的箱子,然后调用equals()与箱子中的对象逐个比较,直到找出相等的对象或者遍历了一遍。
    如果x.equals(y)返回“true”,而x.hashCode() != y.hashCode()会有什么后果呢?假设把x、y方法HashMap,即hashMap.put(x, xValue)和hashMap.put(y, yValue),在hashMap中会存在两个元素x、y,而不是一个。原因很好理解,因为二者的hashCode不同,在hashMap中会放入不用的箱子,从而会被认为是两个对象。
    使用hashCode的目的是为了把对象散列在不同的地方,从而提高检索对象的性能,所以编写一个好的hashCode算法相当重要。在Effective Java 2 edition 中给出了一个简单的hashCode算法:
    1、保存一些常量非0值,例如17,在一个int的result变量中。这个值可以是任意的,但是最好不是0,会增加冲突的可能。
    2、对象每一个重要的域f(就是equals方法使用到那些field)做以下操作:
        a)对域f计算一个int的哈希码c
           i.如果field是boolean型的,计算(f ? 1 : 0)
           ii.如果field是byte,char,int之类的,计算 (int)f
           iii.如果field是long, 计算 (int) (f^(f>>32))
           iv.如果field是float,计算 Float.floatToIntBits(f)
           v.如果field是double,计算Double.doubleToLongBits(f),然后使用2.a.iii的方法计算hash code
           vi.如果field是一个对象的引用,并且这个类的此域比较的equals方法使用了递归的调用equals,直接递归的调用hashCode方法。如果一个更复杂的比较是必须的,对此域计算”canonical representation(规范的表示)”并且在”规范的表示”上调用hashCode(invoke hashCode on the canonical representation)。如果此field为null,返回0。
           vii.如果field时array,将数组中的每个元素作为一个单独的field,用上述规则来计算,并使用2.b中的方式合并这些值。如果每个元素都是significant,你可以使用1.5版本添加的Arrays.hasCode方法。
         b)合并2.a中哈希码c的值到result使用下面的方法:
                   result=31*result+c;
    3、返回result
    4、当你完成hashCode函数之后,自我检验一下相等的实例是否有相等的hash code。编写单元测试去检验你所想的结果。如果相等实例有不同的hash code,找出原因并解决。
    下面给出一个例子,利用上面讲述的东西应该可以解释是什么原因。代码如下:
package learning.collection;

import java.util.HashMap;
import java.util.Map;

/**
 * 测试equals()、hashCode()方法对对象在Collection中的影响。
 * 
 */
public class EqualsAndHashCode
{

    /**
     * @param args
     */
    public static void main(String[] args)
    {
        Point p1 = new Point(1,2);
        Point p2 = new Point(1,2);
        System.out.println("p1.equals(p2):" + p1.equals(p2));
        System.out.println("p1.hashCode():" + p1.hashCode());
        System.out.println("p2.hashCode():" + p2.hashCode());
        Map<Point, String> map1 = new HashMap<Point, String>();
        map1.put(p1, "point 1");
        map1.put(p2, "point 2");
        System.out.println("map1.size():" + map1.size());
        
        BadPoint bp1 = new BadPoint(1,2);
        BadPoint bp2 = new BadPoint(1,2);
        System.out.println("bp1.equals(bp2):" + bp1.equals(bp2));
        System.out.println("bp1.hashCode():" + bp1.hashCode());
        System.out.println("bp2.hashCode():" + bp2.hashCode());
        Map<BadPoint, String> map2 = new HashMap<BadPoint, String>();
        map2.put(bp1, "point 1");
        map2.put(bp2, "point 2");
        System.out.println("map2.size():" + map2.size());
        
        GoodPoint gp1 = new GoodPoint(1,2);
        GoodPoint gp2 = new GoodPoint(1,2);
        System.out.println("gp1.equals(gp2):" + gp1.equals(gp2));
        System.out.println("gp1.hashCode():" + gp1.hashCode());
        System.out.println("gp2.hashCode():" + gp2.hashCode());
        Map<GoodPoint, String> map3 = new HashMap<GoodPoint, String>();
        map3.put(gp1, "point 1");
        map3.put(gp2, "point 2");
        System.out.println("map3.size():" + map3.size());
    }

}

/**
 * 没有重载equals()、hashCode()方法。
 */
class Point 
{
    private int x;
    private int y;
    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    
    public int getX()
    {
        return this.x;
    }
    
    public int getY()
    {
        return this.y;
    }
}

/**
 * 重载了equals()方法,导致equals()返回true,而hashCode不同。
 * 这是一个不好的设计(甚至是错误的)。
 */
class BadPoint
{
    private int x;
    private int y;
    public BadPoint(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    
    public int getX()
    {
        return this.x;
    }
    
    public int getY()
    {
        return this.y;
    }
    
    @Override
    public boolean equals(Object obj)
    {
        if (obj == null) return false;
        if (!(obj instanceof BadPoint)) return false;
        BadPoint objPoint = (BadPoint) obj;
        if (this.x == objPoint.x && this.y == objPoint.y) return true;
        return false;
    }
}

/**
 * 重载了equals()、hashCode()方法。
 */
class GoodPoint
{
    private int x;
    private int y;
    public GoodPoint(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    
    public int getX()
    {
        return this.x;
    }
    
    public int getY()
    {
        return this.y;
    }
    
    @Override
    public boolean equals(Object obj)
    {
        if (obj == null) return false;
        if (!(obj instanceof GoodPoint)) return false;
        GoodPoint objPoint = (GoodPoint) obj;
        if (this.x == objPoint.x && this.y == objPoint.y) return true;
        return false;
    }
    
    @Override
    public int hashCode()
    {
        int result = 17;
        result = 31 * result + this.x;
        result = 31 * result + this.y;
        return result;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Java equals() 和 hashCode() 是两个重要的方法,用于判断对象是否相等和哈希值的计算。 equals() 方法用于比较两个对象是否相等。默认情况下,它使用 == 运算符比较对象的引用,即比较两个对象是否指向同一个内存地址。如果想比较两个对象的属性是否相等,就需要重写 equals() 方法,并根据对象的属性进行比较。 hashCode() 方法用于计算对象的哈希值,即将对象映射为一个整数。哈希值在 Java 经常用于散列表等数据结构,用于快速查找和比较对象。如果两个对象相等,它们的哈希值应该相同。因此,重写 equals() 方法的同时,也需要重写 hashCode() 方法。 在重写 equals() 和 hashCode() 方法时,需要遵循一些规则。比如,如果两个对象相等,它们的 hashCode() 方法应该返回相同的值;反之,如果两个对象的 hashCode() 值相等,它们不一定相等,还需要通过 equals() 方法进行比较。此外,hashCode() 方法不能依赖于对象的内存地址或时间戳等不稳定因素,应该根据对象的属性计算哈希值。 ### 回答2: JavaequalshashCode是非常重要的两个方法,在Java几乎所有的类都会覆盖它们,而且它们是非常重要的类比较和哈希计算的方法equals方法用于比较两个对象是否相等,通常比较两个对象的内容是否相同,他们是否具有相同的属性和值。 equals方法的默认实现是比较两个对象的引用是否相同,即两个对象是否是同一个对象,不过这个默认的实现并不能满足所有的需求,因为有时候即使两个对象的引用不同,但他们的内容依然相同,所以需要重写equals方法来实现内容的比较。 另一方面,hashCode方法则是将一个对象映射成一个整型的哈希值,通常用于快速查找或比较对象。 在Java,哈希表是非常常见的数据结构,而哈希值就是在哈希表用来寻找和比较对象的唯一标识,所以hashCode方法的实现对于哈希表的性能有着非常重要的影响。 需要注意的是,hashCode方法必须和equals方法保持一致性,也就是说如果两个对象的equals方法返回true,那么他们的hashCode方法返回的哈希值必须相同,反之亦然。 在实现equals方法时,一般会遵循一些基本原则,例如对称性,传递性,一致性等,同时还需要注意适当的判断null值和使用instanceof进行类型判断。在实现hashCode方法时,需要确保相同的对象始终返回相同的哈希值,同时也需要尽可能的让不同的对象返回不同的哈希值。 总之,JavaequalshashCode方法是非常重要的两个方法,深入理解其实现原理和使用方式,可以有效提高Java应用程序的性能和稳定性。 ### 回答3: Javaequals()和hashCode()是两个非常重要的方法,它们用于判断对象之间的相等性和排序性。 equals()方法是用来比较两个对象是否相等的,它的作用是比较两个对象的内容是否相等,而不是比较两个对象引用是否相等。在默认情况下,equals()方法会比较两个对象的引用,即判断两个对象是否指向同一个内存地址。但是,我们通常需要自己来重写equals()方法,以便在比较对象时只比较对象的内容。 hashCode()方法Java的哈希函数,它将对象映射到一个整数值。这个整数值通常用于将对象存储到哈希表,可以快速地定位对象。如果两个对象相等,那么它们的hashCode()方法返回的整数值也应该相等。因此,在重写equals()方法时,我们也需要重写hashCode()方法。 在Java,如果两个对象调用equals()方法返回true,那么它们的hashCode()方法返回的整数值也必须相等。因此,重写hashCode()方法时,必须保证相等的对象返回相等的hashCode()值,否则将会影响哈希表的性能。同时,hashCode()方法的重写也应该考虑到对象的内容,以便产生尽可能不同的哈希值。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值