当在HashMap中多次使用一个键时,会产生无限循环



HashMap进入无限循环

我不能理解为什么HashMap抛出stackoverflow错误时,相同的键被多次使用。

代码:

import java.util.HashMap;
public class Test {
    public static void main(String[] args) {
        HashMap hm = new HashMap();
        hm.put(hm, "1");
        hm.put(hm, "2");
    }
} 
错误:

线程"main"异常java.lang.StackOverflowError

不能将Map本身添加为键。从javadoc:

这个禁止的一个特殊情况是不允许map包含自身作为键


问题是您使用的键不是标准对象(任何具有良好定义的equalshashCode方法的对象,即不是映射本身),而是映射本身。

问题是HashMaphashCode是如何计算的:

public int hashCode() {
   int h = 0;
   Iterator<Entry<K,V>> i = entrySet().iterator();
   while (i.hasNext())
       h += i.next().hashCode();
   return h;
}

可以看到,为了计算映射的hashCode,它遍历映射的所有元素。对于每个元素,它计算hashCode。因为映射中唯一具有键的元素是映射本身,所以它成为递归的来源。

将map替换为另一个可以用作key的对象(具有良好定义的equalshashCode)将会工作:

import java.util.HashMap;
 public class Test {
   public static void main(String[] args) {
     HashMap hm = new HashMap();
     String key = "k1";
     hm.put(key, "1");
     hm.put(key, "2");
   }
} 

问题不在于哈希映射会因为两次输入"相同的键"而破坏堆栈,而是因为您选择了特定的映射键。将哈希映射添加到自身。

为了更好地解释- Map契约的一部分是键不能以影响其equals(或hashCode)方法的方式更改。

当您将map添加到自身作为键时,您以一种方式更改了key (map),使其返回与首次添加map时不同的hashCode。

关于更多信息,这是来自JDK dock的Map接口:

注意:如果使用可变对象作为映射键,必须非常小心。如果对象的值以影响相等比较的方式更改,而该对象是映射中的键,则不指定映射的行为。这种禁止的一个特殊情况是,不允许map将自身包含为键。虽然允许map将自身包含为一个值,但建议非常小心:equals和hashCode方法不再在这样的map上定义良好。

为了在HashMap中找到一个键(这是在调用putgetcontainsKey时完成的),为该键调用hashCode方法。

对于HashMap, hashCode()Map所有条目的hashCode()的函数,每个条目的hashCode是键和值的hashCode的函数。由于您的密钥是同一个HashMap实例,计算该密钥的hashCode会导致hashCode方法调用的无限递归,从而导致StackOverflowError

使用HashMap作为HashMap的密钥是一个坏主意。

您使用的是与键相同的对象名称,因此它是无限循环。

是堆栈溢出。

您使用hashmap本身作为键->这意味着递归->没有退出条件-> StackOverflow。

只需使用一个键(Long, String, Object,任何你想要的)。

是的,正如Seek Addo建议的那样,添加带有<>括号的类型

最新更新