You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何以下代码抛出java.lang.StackOverflowError?内部原理解析

为什么 Set s = new HashSet(); s.add(s); s.add(s); 会抛出 StackOverflowError?

这问题戳中了HashSet底层实现的一个有趣递归陷阱,咱们一步步拆解来看:

先搞懂HashSet的底层逻辑

HashSet本质是靠HashMap实现的——当你调用add()方法时,其实是把要添加的元素作为HashMap的key,value则是一个固定的空对象(PRESENT)。所以add()的核心逻辑依赖HashMap的put()方法,这个方法会做两件关键事:

  • 计算key的哈希值(用来定位HashMap的桶位置)
  • 检查是否已存在相同的key(通过哈希值+equals()判断)

触发栈溢出的核心:无限递归计算哈希码

HashSet继承自AbstractSet,而AbstractSet的hashCode()方法实现是这样的:

public int hashCode() {
    int h = 0;
    Iterator<E> i = iterator();
    while (i.hasNext()) {
        E obj = i.next();
        if (obj != null)
            h += obj.hashCode();
    }
    return h;
}

简单说:集合的哈希码 = 集合内所有元素哈希码的总和。

现在看代码的执行流程:

  1. Set s = new HashSet(); → 初始化一个空HashSet,内部的HashMap也是空的。
  2. s.add(s); → 第一次添加自己。此时计算key(s)的哈希码:因为集合是空的,hashCode()遍历空集合后返回0,HashMap顺利把s作为key存入,此时集合里有了一个元素——就是s自己。
  3. s.add(s); → 第二次添加自己。这时候需要先计算key(s)的哈希码:
    • 调用s的hashCode(),遍历集合内的元素(也就是s自己)
    • 遍历到s时,需要计算它的哈希码,也就是再次调用s的hashCode()
    • 又要遍历集合内的元素(还是s自己),再次触发hashCode()调用
    • 无限递归下去,栈的调用深度越来越大,最终超过JVM的栈容量,抛出StackOverflowError

从报错信息看递归链

你提供的报错信息已经清晰暴露了递归路径:

Exception in thread "main" java.lang.StackOverflowError
at java.util.HashMap$KeyIterator.<init>(HashMap.java:1459)
at java.util.HashMap$KeySet.iterator(HashMap.java:916)
at java.util.HashSet.iterator(HashSet.java:172)
at java.util.AbstractSet.hashCode(AbstractSet.java:122)
...

这条链的逻辑是:
AbstractSet.hashCode() → 调用HashSet.iterator() → 获取HashMap的KeyIterator → 在hashCode()里遍历元素,调用元素的hashCode() → 又回到AbstractSet.hashCode(),循环往复直到栈溢出。

总结来说:把集合自身添加到集合后,计算集合哈希码时会触发无限递归,最终导致栈溢出。

内容的提问来源于stack exchange,提问作者Raj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:14:02