用equals()和hashcode()比较Java对象

在这个Java Challenger中,您将学习equals()hashcode()结合如何使Java程序中的对象比较高效而又容易。 简而言之,这些方法可以共同验证两个对象是否具有相同的值。

如果没有equals()hashcode()我们将不得不创建非常大的“ if ”比较,以比较对象的每个字段。 这会使代码真正混乱并且难以阅读。 这两种方法共同帮助我们创建了更灵活,更紧密的代码。

获取Java Challengers源代码

在Java中重写equals()和hashcode()

方法重写是一种技术,其中为了利用多态性 ,在子类中再次编写(重写)了父类或接口的行为。 Java中的每个Object都包含一个equals()hashcode()方法,但是必须重写它们才能正常工作。

为了了解覆盖如何与equals()hashcode() ,我们可以研究它们在核心Java类中的实现。 下面是Object类中的equals()方法。 该方法正在检查当前实例是否与先前传递的Object相同。


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

如果未覆盖hashcode()方法,则将调用Object类中的默认方法。 这是一个本地方法 ,这意味着它将以另一种语言(如C)执行,并将返回有关该对象的内存地址的一些代码。 (除非您正在编写JDK代码,否则确切地知道此方法的工作原理并不重要。)


@HotSpotIntrinsicCandidate
public native int hashCode();

当不覆盖equals()hashcode()方法时,您会看到上述方法被调用。 在这种情况下,这些方法不能满足equals()hashcode()的实际目的,即检查两个或多个对象是否具有相同的值。

通常,当覆盖equals()还必须覆盖hashcode()

用equals()比较对象

我们使用equals()方法比较Java中的对象。 为了确定两个对象是否相同, equals()比较这些对象的属性值:


public class EqualsAndHashCodeExample {

    public static void main(String... equalsExplanation) {
        System.out.println(new Simpson("Homer", 35, 120)
                 .equals(new Simpson("Homer",35,120)));
        
        System.out.println(new Simpson("Bart", 10, 120)
                 .equals(new Simpson("El Barto", 10, 45)));
        
        System.out.println(new Simpson("Lisa", 54, 60)
                 .equals(new Object()));
    }
	
    static class Simpson {

        private String name;
        private int age;
        private int weight;

        public Simpson(String name, int age, int weight) {
            this.name = name;
            this.age = age;
            this.weight = weight;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Simpson simpson = (Simpson) o;
            return age == simpson.age &&
                    weight == simpson.weight &&
                    name.equals(simpson.name);
        }
    }

}

在第一次比较中, equals()将当前对象实例与已传递的对象进行比较。 如果两个对象的值相同,则equals()将返回true

在第二个比较中, equals()检查传递的对象是否为null ,或者是否将其键入为其他类。 如果是不同的类,则对象不相等。

最后, equals()比较对象的字段。 如果两个对象具有相同的字段值,则这些对象相同。

分析对象比较

现在,让我们在main()方法中查看这些比较的结果。 首先,我们比较两个Simpson对象:


System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120)));

这里的对象是相同的,因此结果将为true

接下来,我们再次比较两个Simpson对象:


System.out.println(new Simpson ("Bart", 10, 45).equals(new Simpson ("El Barto", 10, 45))); 

这里的对象几乎相同,但名称不同:Bart和El Barto。 因此结果将是false

最后,让我们比较一下Simpson对象和Object类的实例:


System.out.println(new Simpson ("Lisa", 54, 60).equals(new Object ())); 

在这种情况下,结果将为false因为类类型不同。

equals()与==

乍看之下, ==运算符和equals()方法似乎可以完成相同的操作,但实际上它们的工作方式不同。 ==运算符比较两个对象引用是否指向同一对象。 例如:


System.out.println(homer == homer2);

在第一次比较中,我们使用new运算符实例化了两个不同的Simpson实例。 因此,变量homerhomer2将指向内存堆中的不同Object引用。 因此,结果将为false

System.out.println(homer.equals(homer2));

在第二个比较中,我们重写equals()方法。 在这种情况下,仅会比较名称。 由于两个Simpson对象的名称均为“ Homer”,因此结果为true

用hashcode()唯一标识对象

比较对象时,我们使用hashcode()方法来优化性能。 执行hashcode()将为程序中的每个对象返回唯一的ID,这使比较对象的整个状态的任务变得更加容易。

如果一个对象的哈希码与另一个对象的哈希码不同,则没有理由执行equals()方法:您只知道两个对象是不同的。 在另一方面,如果哈希码相同的,那么你必须执行equals()方法来确定的值和字段是否相同。

这是一个带有hashcode()的实际示例。


public class HashcodeConcept {

    public static void main(String... hashcodeExample) {
        Simpson homer = new Simpson(1, "Homer");
        Simpson bart = new Simpson(2, "Homer");

        boolean isHashcodeEquals = homer.hashCode() == bart.hashCode();

        if (isHashcodeEquals) {
            System.out.println("Should compare with equals method too.");
        } else {
            System.out.println("Should not compare with equals method because " +
                    "the id is different, that means the objects are not equals for sure.");
        }
    }

     static class Simpson {
        int id;
        String name;

        public Simpson(int id, String name) {
            this.id = id;
            this.name = name;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Simpson simpson = (Simpson) o;
            return id == simpson.id &&
                    name.equals(simpson.name);
        }

        @Override
        public int hashCode() {
            return id;
        }
    }
}

始终返回相同值的hashcode()是有效的,但效果不是很好。 在这种情况下,比较将始终返回true ,因此将始终执行equals()方法。 在这种情况下没有性能改进。

对集合使用equals()和hashcode()

Set接口负责确保在Set子类中不会插入任何重复的元素。 以下是一些实现Set接口的类:

Set只能插入唯一的元素,因此,例如,如果要将元素添加到HashSet类中,则必须首先使用equals()hashcode()方法来验证该元素是否唯一。 如果在这种情况下未覆盖equals()hashcode()方法,则可能会在代码中插入重复的元素。

在下面的代码中,我们使用add方法向HashSet对象添加一个新元素。 在添加新元素之前, HashSet检查该元素是否已存在于给定的集合中:


if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
       break;
       p = e; 

如果对象相同,则不会插入新元素。

杂凑集

Set不是唯一使用equals()hashcode()集合。 HashMapHashtableLinkedHashMap也需要这些方法。 通常,如果看到前缀为“哈希”的集合,则可以确定它需要重写hashcode()equals()方法以使其功能正常工作。

使用equals()和hashcode()的准则

您应该仅对具有相同唯一哈希码ID的对象执行equals()方法。 当哈希码ID 同时,不应执行equals()

表1.哈希码比较

如果hashcode()比较... 然后 …
返回true 执行equals()
返回假 不执行equals()

由于性能原因,此原理主要用于SetHash集合中。

对象比较规则

hashcode()比较返回falseequals()方法还必须返回false 。 如果哈希码不同,则对象肯定不相等。

表2.使用hashcode()进行对象比较

当哈希码比较返回时... equals()方法应返回...
真正 对或错

equals()方法返回true ,表示对象的所有值和属性都相等。 在这种情况下,哈希码比较也必须为true。

表3.使用equals()进行对象比较

equals()方法返回时... hashcode()方法应返回...
真正 真正
对或错

接受equals()和hashcode()挑战!

现在该使用equals()hashcode()方法测试您的技能了。 您在此挑战中的目标是找出两个equals()方法比较的输出并猜测Set集合的大小。

首先,请仔细研究以下代码:


public class EqualsHashCodeChallenge {

    public static void main(String... doYourBest) {
        System.out.println(new Simpson("Bart").equals(new Simpson("Bart")));
        Simpson overriddenHomer = new Simpson("Homer") {
            public int hashCode() {
                return (43 + 777) + 1;
            }
        };

        System.out.println(new Simpson("Homer").equals(overriddenHomer));

        Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge")));
        set.add(new Simpson("Homer"));
        set.add(overriddenHomer);

        System.out.println(set.size());
    }

    static class Simpson {
        String name;

        Simpson(String name) {
            this.name = name;
        }

        @Override
        public boolean equals(Object obj) {
            Simpson otherSimpson = (Simpson) obj;
            return this.name.equals(otherSimpson.name) &&
                    this.hashCode() == otherSimpson.hashCode();
        }

        @Override
        public int hashCode() {
            return (43 + 777);
        }
    }

}

请记住,首先分析代码,猜测结果, 然后运行代码 。 您的目标是提高代码分析技能,并吸收Java核心概念以使代码更强大。 在检查以下正确答案之前,请选择您的答案。


A) 
true 
true 
4 

B) 
true 
false 
3 

C) 
true 
false 
2

D) 
false 
true 
3 

刚才发生了什么? 了解equals()和hashcode()

在第一次equals()方法比较中,结果为true因为对象的状态完全相同,并且hashcode()方法为两个对象返回相同的值。

在第二个equals()方法比较中, hashcode()方法被overridenHomer变量overridenHomer 。 名称是“荷马”两个Simpson对象,但是hashcode()方法返回一个不同的值overriddenHomer 。 在这种情况下, equals()方法的最终结果将为false因为该方法包含与哈希码的比较。

您可能会注意到,集合的大小设置为可容纳三个Simpson对象。 让我们详细检查一下。

集合中的第一个对象将被正常插入:


new Simpson("Homer");

同样,下一个对象也将正常插入,因为它具有与上一个对象不同的值:


new Simpson("Marge");

最后,以下Simpson对象具有与第一个对象相同的值。 在这种情况下,将不会插入对象:


set.add(new Simpson("Homer"));

众所周知, overridenHomer对象使用与常规Simpson(“Homer”)实例化不同的哈希码值。 因此,此元素将被插入到集合中:


overriddenHomer;

答案键

这个Java挑战者的答案是B。 输出为:


true 
false 
3 

视频挑战! 调试equals()和hashcode()

调试是完全吸收编程概念并改善代码的最简单方法之一。 在此视频中,您可以在调试和解释Java equals()hashcode()挑战的同时进行后续操作。

翻译自: https://www.infoworld.com/article/3305792/comparing-java-objects-with-equals-and-hashcode.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值