Kotlin中ArrayList<HashSet<data class>>的reduce函数处理子集时失效
解决ArrayList<HashSet>交集函数处理子集失效的问题
我一眼就看出问题出在你用retainAll的方式上——这个方法会直接修改调用它的集合本身,而不是返回一个新集合。你的reduce操作里,acc其实是原ArrayList中第一个HashSet的引用,调用acc.retainAll(it)后,原集合的内容被永久改变了。这就导致当你后续再处理这个ArrayList的子集时,里面的集合已经不是最初的原始数据,自然算不出正确的交集。
修复方案:操作集合的副本而非原集合
我们需要在每次交集计算时,基于原集合的副本进行操作,避免破坏原始数据。这里用fold来实现会更清晰,因为可以明确初始化一个独立的副本:
fun intersection(data: ArrayList<HashSet<Protein>>): HashSet<Protein> { // 处理空输入的边界情况 if (data.isEmpty()) return HashSet() // 以第一个集合的副本作为初始累加器,完全独立于原集合 return data.drop(1).fold(HashSet(data.first())) { acc, currentSet -> // 在副本上执行交集操作,不影响原数据 acc.retainAll(currentSet) acc } }
为什么这样能解决问题?
- 避免修改原集合:
HashSet(data.first())创建了第一个集合的独立副本(对于你的Protein类,因为是data class且属性都是不可变类型,浅拷贝足够),后续所有操作都在这个副本上进行,原ArrayList中的HashSet内容完全不受影响。 - 正确处理子集场景:不管你传入的是整个ArrayList还是它的某个子集(比如
data.subList(0,3)),原始集合的数据都不会被改动,每次计算都是基于当前传入的原始子集数据进行的。
额外提示
如果你坚持要用reduce,也可以修改为每次创建新集合:
fun intersection(data: ArrayList<HashSet<Protein>>): HashSet<Protein> { return if (data.isEmpty()) HashSet() else data.reduce { acc, currentSet -> HashSet(acc).apply { retainAll(currentSet) } } }
不过这种方式每次reduce都会创建新集合,性能上比fold略差一点,因为fold只需要初始化一次副本。
内容的提问来源于stack exchange,提问作者PeptideWitch
相关产品推荐
相关产品推荐

