【发布时间】:2016-11-08 16:19:10
【问题描述】:
根据最近的研究,我的理解是 hashCode 是一种方法,它为针对特定数据类型实例化的对象返回唯一整数。但是为所有数据类型对象计算哈希码将使用相同的算法还是会根据某些逻辑而变化?我没有得到太多关于collection datatypes的hashcode计算机制的信息。如果有任何澄清或参考将不胜感激。
【问题讨论】:
标签: java collections hashcode
根据最近的研究,我的理解是 hashCode 是一种方法,它为针对特定数据类型实例化的对象返回唯一整数。但是为所有数据类型对象计算哈希码将使用相同的算法还是会根据某些逻辑而变化?我没有得到太多关于collection datatypes的hashcode计算机制的信息。如果有任何澄清或参考将不胜感激。
【问题讨论】:
标签: java collections hashcode
计算hashCode 取决于类。没有标准规则。
你只需要遵守here解释的规则:
hashCode的总合约是:
- 只要在 Java 应用程序执行期间对同一个对象多次调用,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上的 equals 比较中使用的信息。该整数不需要在应用程序的一次执行与同一应用程序的另一次执行之间保持一致。
- 如果两个对象根据 equals(Object) 方法相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。
- 如果根据 equals(java.lang.Object) 方法,如果两个对象不相等,则不要求对两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
这部分答案是为了解释对Qwerky的答案的一个评论,抱歉但不容易在cmets上展示完整的代码片段。
这里有一个简单的代码,解释了为什么List 不能用作地图中的键。
Map<List, String> map = new HashMap<List, String>();
List list = new ArrayList();
map.put(list, "a");
System.out.println("Before: " + map.get(list)); // Print A
list.add(new Object());
System.out.println("After: " + map.get(list)); // Print null
请注意,列表遵循hashCode的合同。
但是正如 Brian Goetz 和 Josh Bloch 所指出的:
如果一个对象的
hashCode()值可以根据它的状态而改变,那么我们在使用这些对象作为基于哈希的集合中的键时必须小心,以确保我们不允许它们在使用时改变它们的状态作为哈希键。 所有基于散列的集合都假定对象的散列值在用作集合中的键时不会改变。 如果键的哈希码在集合中发生变化,可能会出现一些不可预测和令人困惑的后果。这在实践中通常不是问题 - 在 HashMap 中使用像 List 这样的可变对象作为键并不常见。
【讨论】:
hashCode() 和equals() (对于HashMap) 和compareTo() (对于TreeMap()),密钥必须是不可变的要求适用于所有种键,而不仅仅是列表。 任何用作键的对象当对象是键时不得更新。 --- 另外,问题根本不是关于地图的,那为什么还要谈论地图键的限制呢?
List 没有遵循合同的哪一部分?它遵循所有 3 条规则,没有失败。正如已经向您评论的那样,第一条规则说 “hashCode 方法必须始终返回相同的整数,提供了没有修改对象上的 equals 比较中使用的信息”。 List 完全符合该规则,即哈希码保持不变,只要列表或其任何元素未更改。您的代码示例丢失数据是因为 you 违反了规则,而不是 List。 您更改了列表。这不是 List 的错,它按照规则做的一切都是正确的。
您好,默认的 hashcode 方法返回对象引用为 classname@Soome 唯一的数值,但是在收集的情况下,我们重写 hashcode 方法来生成我们的自定义键,同时我们重写 equals 方法来获取正确的输出。
你可以从我下面的代码中得到详细的想法
package com.rk.collections;
import java.util.HashMap;
import java.util.Map;
class Employee
{
private String name;
private int regdno;
private String city;
private String college;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getRegdno() {
return regdno;
}
public void setRegdno(int regdno) {
this.regdno = regdno;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCollege() {
return college;
}
public void setCollege(String college) {
this.college = college;
}
@Override
public int hashCode() {
return regdno;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (regdno != other.regdno)
return false;
return true;
}
public String toString() {
return "Employee [name=" + name + ", regdno=" + regdno + ", city=" + city + ", college=" + college + "]";
}
}
public class MapDemo {
public static void main(String []args)
{
Employee e1=new Employee();
e1.setName("Amlan");
e1.setRegdno(326);
e1.setCity("Bhanjanagar");
e1.setCollege("IGIT");
Employee e2=new Employee();
e2.setName("Amlan");
e2.setRegdno(325);
e2.setCity("Sambalpur");
e2.setCollege("IGIT");
Employee e3=new Employee();
e3.setName("Tapas");
e3.setRegdno(324);
e3.setCity("Baripada");
e3.setCollege("IGIT");
HashMap<Employee,Employee> hs=new HashMap();
hs.put(e1,e1);
hs.put(e2,e2);
hs.put(e3,e3);
System.out.println(hs.size());
for(Map.Entry m:hs.entrySet())
{
System.out.println(m.getValue());
}
}
}
【讨论】:
对于它的价值,这是AbstractList 如何计算它的 hashCode;
/**
* Returns the hash code value for this list.
*
* <p>This implementation uses exactly the code that is used to define the
* list hash function in the documentation for the {@link List#hashCode}
* method.
*
* @return the hash code value for this list
*/
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}
此外,哈希码不一定是唯一的。
【讨论】:
provided no information used in equals comparisons on the object is modified。如果两个列表相等并且您更改其中一个列表,则它们不再相等,因此它们应该具有不同的哈希码。