最近我读了这篇文章 开发人员工作文件。
该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。
我如何决定有效地实现这些方法?
最近我读了这篇文章 开发人员工作文件。
该文档是关于有效和正确地定义hashCode()和equals(),但我无法弄清楚为什么我们需要覆盖这两个方法。
我如何决定有效地实现这些方法?
当前回答
你必须重写hashCode()在每个 重写equals()的类。失败 这样做会导致违反 总合同 Object.hashCode(),它将防止 你的类不能正常运行 结合所有基于哈希的 集合,包括HashMap, HashSet和Hashtable。 摘自Joshua Bloch的《Effective Java》
通过一致地定义equals()和hashCode(),可以提高类作为基于散列的集合中的键的可用性。正如hashCode的API文档所解释的那样:“支持此方法是为了受益于诸如java.util.Hashtable所提供的哈希表。”
关于如何有效地实现这些方法的问题,最好的答案是建议你阅读《Effective Java》的第3章。
其他回答
Bah -“你必须在每个重写equals()的类中重写hashCode()。”
[出自Joshua Bloch的《Effective Java》?]
Isn't this the wrong way round? Overriding hashCode likely implies you're writing a hash-key class, but overriding equals certainly does not. There are many classes that are not used as hash-keys, but do want a logical-equality-testing method for some other reason. If you choose "equals" for it, you may then be mandated to write a hashCode implementation by overzealous application of this rule. All that achieves is adding untested code in the codebase, an evil waiting to trip someone up in the future. Also writing code you don't need is anti-agile. It's just wrong (and an ide generated one will probably be incompatible with your hand-crafted equals).
他们肯定应该在被写来用作键的对象上强制设置一个接口吗?无论如何,Object永远不应该提供默认的hashCode()和equals() imho。它可能鼓励了许多破碎的散列集合。
但无论如何,我认为“规则”是前后颠倒的。与此同时,我将继续避免使用“等号”进行相等性测试方法:-(
考虑在一个桶中收集所有黑色的球。你的工作是像下面这样给这些球上色,并将其用于适当的游戏,
对于网球-黄色,红色。 板球-白色
现在水桶有三种颜色的球黄色,红色和白色。只有你知道哪个颜色适合哪个游戏。
给球上色-哈希。 选择比赛的球-平等。
如果你给球上色,然后有人选了板球或网球,他们不会介意颜色的!!
身份不是平等。
= operator ==测试标识符。 equals(Object obj)方法比较相等性测试(即我们需要通过重写方法来告诉相等)
为什么我需要重写Java中的equals和hashCode方法?
首先我们要理解等号法的用法。
为了识别两个对象之间的差异,我们需要重写equals方法。
例如:
Customer customer1=new Customer("peter");
Customer customer2=customer1;
customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object
------------------------------
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers.
------------------------------
Now I have overriden Customer class equals method as follows:
@Override
public boolean equals(Object obj) {
if (this == obj) // it checks references
return true;
if (obj == null) // checks null
return false;
if (getClass() != obj.getClass()) // both object are instances of same class or not
return false;
Customer other = (Customer) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference
return false;
return true;
}
Customer customer1=new Customer("peter");
Customer customer2=new Customer("peter");
Insteady identify the Object equality by JVM, we can do it by overring equals method.
customer1.equals(customer2); // returns true by our own logic
现在hashCode方法很容易理解了。
hashCode生成整数,以便将对象存储在HashMap、HashSet等数据结构中。
假设我们有如上所述的override equals Customer方法,
customer1.equals(customer2); // returns true by our own logic
在处理数据结构时,我们将对象存储在桶中(桶是文件夹的花哨名称)。如果我们使用内置哈希技术,对于以上两个客户,它会生成两个不同的哈希码。所以我们把相同的对象存储在两个不同的地方。为了避免这类问题,我们也应该基于以下原则重写hashCode方法。
不相等的实例可能具有相同的hashcode。 相同的实例应该返回相同的hashcode。
我正在研究解释“如果你只覆盖hashCode,那么当你调用myMap.put(first,someValue)时,它首先接受,计算它的hashCode并将其存储在给定的桶中。然后,当你调用myMap.put(first,someOtherValue)时,它应该根据Map文档将first替换为second,因为它们是相等的(根据我们的定义)。”:
我认为第二次添加myMap时应该是第二个对象比如myMap。put(second,someOtherValue)
让我用非常简单的话来解释这个概念。
首先,从更广泛的角度来看,我们有集合,而hashmap是集合中的数据结构之一。
要理解为什么我们必须重写equals和hashcode方法,如果需要的话,首先要理解什么是hashmap以及它的功能。
hashmap是一种以数组方式存储键值对数据的数据结构。假设是a[],其中'a'中的每个元素都是一个键值对。
此外,上述数组中的每个索引都可以是链表,因此在一个索引上有多个值。
为什么要使用hashmap呢?
如果我们必须在一个大数组中搜索,那么搜索每个数组,如果它们不是有效的,那么哈希技术告诉我们,让我们用一些逻辑预处理数组,并根据该逻辑对元素进行分组,即哈希
例如:我们有数组1、2、3、4、5、6、7、8、9、10、11,我们应用哈希函数mod 10,所以1、11将被分组在一起。因此,如果我们必须在前一个数组中搜索11,那么我们必须迭代整个数组,但当我们对它进行分组时,我们限制了迭代的范围,从而提高了速度。为了简单起见,用于存储所有上述信息的数据结构可以看作是一个2d数组
现在除了上面的hashmap还告诉它不会在其中添加任何duplicate。这就是为什么我们要重写等号和hashcode的主要原因
因此,当我们说要解释hashmap的内部工作时,我们需要找到hashmap有什么方法,以及它如何遵循上面我解释过的规则
所以hashmap有一个方法叫as put(K,V),根据hashmap,它应该遵循上面的规则,有效地分配数组,不添加任何重复
put所做的是首先为给定的键生成hashcode来决定值应该放在哪个索引中。如果那个下标处什么都没有,那么新值就会被加到那里,如果那里已经有了,那么新值就会被加到链表末尾那个下标处。但是请记住,不应该根据期望的hashmap行为添加重复项。假设你有两个整数对象aa=11 bb=11。
由于每个对象都派生自对象类,比较两个对象的默认实现是比较引用,而不是对象内部的值。因此,在上述情况下,尽管语义上相同,但两个对象都将无法通过相等性测试,并且有可能存在两个具有相同hashcode和相同值的对象,从而创建重复的对象。如果我们重写,就可以避免添加重复项。 您也可以参考详细工作
import java.util.HashMap;
public class Employee {
String name;
String mobile;
public Employee(String name,String mobile) {
this.name = name;
this.mobile = mobile;
}
@Override
public int hashCode() {
System.out.println("calling hascode method of Employee");
String str = this.name;
int sum = 0;
for (int i = 0; i < str.length(); i++) {
sum = sum + str.charAt(i);
}
return sum;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
System.out.println("calling equals method of Employee");
Employee emp = (Employee) obj;
if (this.mobile.equalsIgnoreCase(emp.mobile)) {
System.out.println("returning true");
return true;
} else {
System.out.println("returning false");
return false;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee emp = new Employee("abc", "hhh");
Employee emp2 = new Employee("abc", "hhh");
HashMap<Employee, Employee> h = new HashMap<>();
//for (int i = 0; i < 5; i++) {
h.put(emp, emp);
h.put(emp2, emp2);
//}
System.out.println("----------------");
System.out.println("size of hashmap: "+h.size());
}
}