Kotlin中ArrayList的set方法未替换元素及Bullet移植对象引用问题
排查Kotlin移植Bullet引擎时ArrayList Set方法不生效的问题
我之前在把C物理引擎移植到Kotlin时也踩过类似的集合操作坑,尤其是从C的数组/容器思维转过来的时候,很容易因为Kotlin集合的特性踩雷。针对你遇到的问题,咱们一步步拆解分析:
首先聚焦stkStack[0] = element不生效的核心原因
1. 自定义resize函数的逻辑大概率是问题根源
Kotlin标准库的ArrayList并没有自带resize方法,所以你肯定是自己实现了扩展函数或者工具方法。这里最常见的坑是:
如果你的
resize是重新创建了一个新的ArrayList实例并赋值给stkStack,比如下面这种错误实现:var stkStack = ArrayList<StkNN>() fun resize(newSize: Int) { val newList = ArrayList<StkNN>(newSize) newList.addAll(stkStack) // 这里直接替换了stkStack的引用! stkStack = newList }那你之前执行的
stkStack[0] = element是修改了旧的ArrayList实例,而resize之后stkStack指向了新的实例,旧实例的修改自然就看不到了。正确的
resize应该是直接修改当前ArrayList的大小,而不是替换引用,比如:fun <T> ArrayList<T>.resize(newSize: Int, defaultValue: T) { while (size < newSize) { add(defaultValue) } while (size > newSize) { removeLast() } }这种实现会直接在原集合上调整大小,之前的
set操作会被保留。
2. 验证索引有效性的小细节
虽然你判断了stkStack.isNotEmpty()才执行set,但调试时可以加个断言确认索引合法:
if (stkStack.isNotEmpty()) { check(stkStack.size > 0) { "stkStack size is 0 after isNotEmpty check" } stkStack[0] = element } else { stkStack += element }
极端情况下(比如多线程修改,但你这是调试HelloWorld应该是单线程),可能出现判断和set之间集合大小变化的情况,加断言能快速定位。
再说说你提到的对象引用相关问题
代码里的pa === pb是判断两个对象的引用是否完全相同,在移植C++的Bullet引擎时,这里容易踩的坑是:
- 如果你的
DbvtNode是通过JNI从C层映射过来的,要确保同一个C指针对应的Kotlin对象是单例的。比如用一个HashMap<Long, DbvtNode>来存储指针地址和Kotlin对象的映射,每次从C拿到指针时,先查映射表,存在就复用,不存在再创建新对象。否则会出现“逻辑上是同一个C节点,但Kotlin里是两个不同对象”的情况,导致===判断不符合预期。
调试建议
- 在
stkStack[0] = element之后,立即打印stkStack[0]的内容和stkStack的identity hash(System.identityHashCode(stkStack)); - 执行
resize之后,再打印一次这两个值,对比是否一致; - 如果identity hash变了,说明
resize替换了集合引用,这就是问题所在。
内容的提问来源于stack exchange,提问作者elect
相关产品推荐
相关产品推荐

