Kotlin中同类型属性Set引发StackOverflow异常问题排查
问题原因分析
你遇到的StackOverflow错误,根源在于data class自动生成的hashCode/equals方法与MutableSet的特性结合,触发了无限递归:
- Kotlin的data class会根据主构造函数里的所有属性,自动生成
hashCode、equals和toString方法。你的Person类主构造函数包含neighbors(MutableSet<Person>),所以生成的hashCode会递归计算集合里每个Person的哈希值。 - MutableSet的实现类(比如默认的
LinkedHashSet)在添加元素时,会先调用元素的hashCode确定存储位置,还会调用equals检查元素是否已存在。
当你让commonNeighbor引用neighbor1/neighbor2,同时这两个又引用commonNeighbor时,就形成了循环引用:计算commonNeighbor的哈希值时,需要遍历它的neighbors里的neighbor1,而计算neighbor1的哈希值又要遍历它的neighbors里的commonNeighbor,无限递归下去直到栈溢出。
为什么替换后问题消失
- 换成普通class:普通class不会自动生成
hashCode/equals,默认使用Object的实现(基于对象引用),添加元素时不会触发递归计算。 - 换成ArrayList:ArrayList添加元素时不会调用元素的
hashCode/equals(只有调用contains、indexOf等方法时才会),所以不会触发递归。
解决方案
方案1:自定义data class的hashCode和equals,排除循环引用的属性
只基于id生成这两个方法,避免递归处理neighbors:
data class Person( val id: Int, val neighbors: MutableSet<Person> = mutableSetOf() ) { override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as Person return id == other.id } override fun hashCode(): Int { return id } }
方案2:将neighbors移出主构造函数
这样data class自动生成的方法不会包含neighbors,也就不会触发递归:
data class Person(val id: Int) { val neighbors: MutableSet<Person> = mutableSetOf() }
方案3:改用普通class(如果不需要data class的其他特性)
class Person( val id: Int, val neighbors: MutableSet<Person> = mutableSetOf() )
内容的提问来源于stack exchange,提问作者Marios Plenchidis
相关产品推荐
相关产品推荐

