Java哈希映射如何使用相同的哈希代码处理不同的对象?
根据我的理解,我认为:Java哈希映射如何使用相同的哈希代码处理不同的对象?,java,hashmap,hashcode,hash-function,Java,Hashmap,Hashcode,Hash Function,根据我的理解,我认为: 两个对象拥有相同的哈希代码是完全合法的 如果两个对象相等(使用equals()方法),则它们具有相同的哈希代码 如果两个对象不相等,则它们不能具有相同的哈希代码 我说得对吗 如果我是对的,我有以下问题: HashMap在内部使用对象的hashcode。因此,如果两个对象可以具有相同的hashcode,那么HashMap如何跟踪它使用的键 有人能解释一下HashMap如何在内部使用对象的hashcode吗?hashcode决定HashMap要检查哪个bucket。如果buc
HashMap
在内部使用对象的hashcode。因此,如果两个对象可以具有相同的hashcode,那么HashMap
如何跟踪它使用的键
有人能解释一下HashMap如何在内部使用对象的hashcode吗?hashcode决定HashMap要检查哪个bucket。如果bucket中有多个对象,则会执行线性搜索以查找bucket中的哪个项目与所需项目相等(使用
equals()
)方法
换句话说,如果您有一个完美的hashcode,那么hashmap访问是恒定的,您将永远不必迭代一个bucket(从技术上讲,您还必须有MAX_INT bucket,Java实现可能在同一个bucket中共享一些hash码,以减少空间需求)。如果你有最差的hashcode(总是返回相同的数字),那么你的hashmap访问就会变成线性的,因为你必须搜索map中的每一项(它们都在同一个bucket中)才能得到你想要的
大多数情况下,编写良好的hashcode并不完美,但它的独特性足以让您或多或少地不断访问。您的第三个断言是不正确的 两个不相等的对象具有相同的哈希代码是完全合法的。它被
HashMap
用作“第一次通过过滤器”,以便映射可以快速找到具有指定键的可能条目。然后测试具有相同哈希代码的密钥是否与指定的密钥相等
您不希望要求两个不相等的对象不能具有相同的哈希代码,否则会将您限制为232个可能的对象。(这也意味着不同的类型甚至不能使用对象的字段来生成哈希代码,因为其他类可以生成相同的哈希。)第三点你错了。两个条目可以具有相同的哈希代码,但不能相等。请看一看的执行情况。您可以看到,它检查哈希值是否相等,键是否相等。如果第三点为真,则无需检查键是否相等。哈希代码在键之前进行比较,因为前者是更有效的比较 如果您有兴趣了解更多关于这方面的内容,请参阅Wikipedia的文章,我相信这是OpenJdk实现使用的机制。这一机制与另一个答案提到的“桶”方法略有不同 hashmap的工作原理如下(这有点简化,但它说明了基本机制): 它有许多“bucket”,用来存储键值对。每个bucket都有一个唯一的编号,这就是bucket的标识。当您将密钥-值对放入映射时,hashmap将查看密钥的哈希代码,并将该对存储在标识符为密钥哈希代码的bucket中。例如:密钥的散列码是235->该对存储在存储桶编号235中。(请注意,一个bucket可以存储多个键值对) 当您在hashmap中查找一个值时,通过给它一个键,它将首先查看您所给键的哈希代码。hashmap随后将查看相应的bucket,然后将您提供的密钥与bucket中所有对的密钥进行比较,方法是将它们与
equals()
进行比较
现在,您可以看到这对于在映射中查找键值对是如何非常有效:通过键的哈希代码,哈希映射立即知道要在哪个bucket中查找,因此它只需根据该bucket中的内容进行测试
查看上述机制,您还可以看到键的hashCode()
和equals()
方法需要哪些要求:
- 如果两个键相同(
在比较它们时返回equals()
),则它们的true
方法必须返回相同的数字。如果键违反了这一点,那么相等的键可能存储在不同的bucket中,hashmap将无法找到键值对(因为它将在同一个bucket中查找)hashCode()
- 如果两个键不同,那么它们的哈希代码是否相同并不重要。如果它们的哈希代码相同,它们将存储在同一个bucket中,在这种情况下,hashmap将使用
来区分它们equals()
HashMap
是Entry
对象的数组
将HashMap
视为一个对象数组
看看这个对象是什么:
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next;
final int hash;
…
}
创建的阵列大小为16,默认负载平衡为0.75
添加新的键值对
计算密钥的哈希代码
计算位置hash%(arrayLength-1)
元素应放置的位置(桶号)
如果您尝试使用已保存在HashMap
中的键添加值,t
HashMap hashMap = new HashMap();
import java.util.HashMap;
public class Students {
String name;
int age;
Students(String name, int age ){
this.name = name;
this.age=age;
}
@Override
public int hashCode() {
System.out.println("__hash__");
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
System.out.println("__eq__");
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Students other = (Students) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
public static void main(String[] args) {
Students S1 = new Students("taj",22);
Students S2 = new Students("taj",21);
System.out.println(S1.hashCode());
System.out.println(S2.hashCode());
HashMap<Students,String > HM = new HashMap<Students,String > ();
HM.put(S1, "tajinder");
HM.put(S2, "tajinder");
System.out.println(HM.size());
}
}
Output:
__ hash __
116232
__ hash __
116201
__ hash __
__ hash __
2
public static void main(String[] args) {
Students S1 = new Students("taj",21);
Students S2 = new Students("taj",21);
System.out.println(S1.hashCode());
System.out.println(S2.hashCode());
HashMap<Students,String > HM = new HashMap<Students,String > ();
HM.put(S1, "tajinder");
HM.put(S2, "tajinder");
System.out.println(HM.size());
}
}
Now lets change out main method a little bit. Output after this change is
__ hash __
116201
__ hash __
116201
__ hash __
__ hash __
__ eq __
1
We can clearly see that equal method is called. Here is print statement __eq__, since we have same hashcode, then content of objects MAY or MAY not be similar. So program internally calls Equal method to verify this.
Conclusion
If hashcode is different , equal method will not get called.
if hashcode is same, equal method will get called.
Thanks , hope it helps.
static int hashToBucket(int tableSize, int hash) {
return (tableSize - 1) & hash;
}
int hash = hash(hashValue)
{
“girl” => “ahhan” ,
“misused” => “Manmohan Singh” ,
“horsemints” => “guess what”,
“no” => “way”
}
1) reverse the string.
2) keep on multiplying ascii of each character with increasing power of 31 . then add the components .
3) So hashCode() of girl would be –(ascii values of l,r,i,g are 108, 114, 105 and 103) .
e.g. girl = 108 * 31^0 + 114 * 31^1 + 105 * 31^2 + 103 * 31^3 = 3173020
Array Index 0 –
Array Index 1 - LinkedIst (“ahhan” , “Manmohan Singh” , “guess what”)
Array Index 2 – LinkedList (“way”)
Array Index 3 –
Array Index 0 –
Array Index 1 - LinkedIst (<”girl” => “ahhan”> , <” misused” => “Manmohan Singh”> , <”horsemints” => “guess what”>)
Array Index 2 – LinkedList (<”no” => “way”>)
Array Index 3 –
/**
* Implements Map.get and related methods
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
/**
* Implements Map.put and related methods
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
myHashmap.put("old","old-value");
myHashMap.put("very-old","very-old-value");
myHashmap.put("old","old-value");
hash XOR hash >>> 16
class Entry{
final Key k;
value v;
final int hash;
Entry next;
}
myHashmap.put("very-old","very-old-value");