Groovy中集合减法如何仅单次扣除元素以保留重复项?
问题分析与解决方案
为什么原代码得到空列表?
Groovy里List的-操作符(对应minus()方法)的行为和你预期的不一样——它会移除左边列表中所有出现在右边列表里的元素,不管该元素在左边出现多少次。
看你的代码执行后:
l1最终是[0, 0, 1, 1, 2, 2, 3, 3, 4, 4](每个数字被添加了两次)l2是[0, 1, 2, 3, 4](每个数字出现一次)
执行l1 - l2时,会把l1里所有的0、1、2、3、4全部移除,所以结果自然是空列表。
实现“每个元素仅扣除一次”的解决方案
如果你想让l2中的每个元素只从l1里移除一次,可以手动遍历l2,逐个从l1的副本中移除第一个匹配的元素(避免修改原列表):
def l1 = [] as List; def l2 = [] as List; for(int i = 0; i < 5; i++) { l1 << i; l2 << i; l1 << i; } println(l1); // 输出: [0, 0, 1, 1, 2, 2, 3, 3, 4, 4] println(l2); // 输出: [0, 1, 2, 3, 4] // 创建l1的副本,避免修改原列表 def result = new ArrayList<>(l1) // 遍历l2的每个元素,从副本中移除一次 l2.each { element -> result.remove(element) // 只移除第一个匹配到的元素 } println(result); // 输出: [0, 1, 2, 3, 4]
另一种灵活方式:基于元素计数构建结果
如果l2中元素可能存在重复,或者需要更精细的次数控制,可以先统计l1中每个元素的出现次数,再减去l2中对应的次数,最后重新构建列表:
def countMap = l1.countBy { it } // 统计每个元素的出现次数: [0:2, 1:2, 2:2, 3:2, 4:2] l2.each { element -> if (countMap[element] > 0) { countMap[element]-- // 每个元素次数减1 } } // 根据剩余次数构建结果列表 def result = countMap.flatMap { key, count -> [key] * count } println(result); // 输出: [0, 1, 2, 3, 4]
总结
Groovy默认的-操作符是全量匹配移除,若需要按“每个元素仅移除一次”的逻辑处理,需要手动实现遍历移除或基于计数的方式,而不能直接依赖默认的minus操作。
内容的提问来源于stack exchange,提问作者xunitc
相关产品推荐
相关产品推荐

