HashSet遍历移除多元素及循环素数代码技术咨询
嘿,我来帮你搞定这两个问题——先解决遍历HashSet时迭代中移除多个元素的核心痛点,再拆解你那段循环素数代码里的问题。
一、迭代HashSet时安全移除多个元素的正确方式
直接在Iterator遍历过程中调用集合的removeAll()或者手动修改集合,大概率会触发ConcurrentModificationException——这是HashSet的快速失败机制在起作用,防止迭代过程中集合结构被意外修改。要安全移除多个元素,有几种靠谱的做法:
方法1:先收集待移除元素,遍历结束后统一处理
这种方式最稳妥,先把所有要移除的元素存到临时集合里,等迭代完再一次性从原集合中移除:
Set<Integer> primes = getPrimes(maxPlusOne); Set<Integer> toRemove = new HashSet<>(); Set<Integer> circularPrimes = new HashSet<>(); Iterator<Integer> it = primes.iterator(); while(it.hasNext()) { Integer p = it.next(); // 如果当前元素已经标记要移除,直接用Iterator的remove()删掉 if (toRemove.contains(p)) { it.remove(); continue; } Set<Integer> perms = getAllRotations(p); if (primes.containsAll(perms)) { circularPrimes.addAll(perms); toRemove.addAll(perms); // 把所有旋转数标记为待移除 } } // 最后统一移除所有标记的元素 primes.removeAll(toRemove);
方法2:遍历原集合的快照,直接修改原集合
复制一份原集合的快照,遍历快照的时候可以放心修改原集合,因为快照和原集合是独立的:
Set<Integer> primes = getPrimes(maxPlusOne); Set<Integer> primesSnapshot = new HashSet<>(primes); Set<Integer> circularPrimes = new HashSet<>(); Iterator<Integer> it = primesSnapshot.iterator(); while(it.hasNext()) { Integer p = it.next(); // 如果原集合里已经没这个元素了,说明之前已经处理过,跳过 if (!primes.contains(p)) continue; Set<Integer> perms = getAllRotations(p); if (primes.containsAll(perms)) { circularPrimes.addAll(perms); primes.removeAll(perms); // 直接修改原集合,不会影响快照的迭代 } }
方法3:Java 8+ 用Stream简化操作
如果你的项目用Java 8及以上,用Stream可以更简洁地实现,还能避免迭代器的繁琐:
Set<Integer> primes = getPrimes(maxPlusOne); Set<Integer> circularPrimes = new HashSet<>(); primes.stream() // 跳过已经加入循环素数集合的元素,避免重复处理 .filter(p -> !circularPrimes.contains(p)) .forEach(p -> { Set<Integer> rotations = getAllRotations(p); if (primes.containsAll(rotations)) { circularPrimes.addAll(rotations); } }); // 最后把循环素数从原素数集合中移除 primes.removeAll(circularPrimes);
二、你的循环素数代码存在的问题分析
看你给出的代码片段,有几个明显的坑需要注意:
并发修改异常风险:如果后续你想在迭代中用
primes.removeAll(perms)移除已处理的旋转素数,直接这么写会立刻抛出ConcurrentModificationException——因为Iterator不允许在迭代过程中通过集合本身的方法修改结构。重复处理浪费性能:比如当你处理123时,把123、231、312都加入了
circularPrimes,但后续迭代到231时,又会再次处理它的旋转数,做重复的判断和操作,完全没必要。代码不完整:你写的
circularPrimes.addA...应该是circularPrimes.addAll(perms)吧?如果只add单个元素,会漏掉其他旋转素数,导致结果不全。
修正后的完整示例代码
结合上面的方法1,给你一个完整的可运行版本:
// 获取小于maxPlusOne的所有素数 Set<Integer> primes = getPrimes(maxPlusOne); Set<Integer> circularPrimes = new HashSet<>(); Set<Integer> processedPrimes = new HashSet<>(); Iterator<Integer> iterator = primes.iterator(); while (iterator.hasNext()) { Integer currentPrime = iterator.next(); // 跳过已经处理过的素数 if (processedPrimes.contains(currentPrime)) { iterator.remove(); continue; } // 获取当前素数的所有旋转数 Set<Integer> rotations = getAllRotations(currentPrime); // 检查所有旋转数是否都是素数 boolean isCircularPrime = true; for (Integer rot : rotations) { if (!primes.contains(rot)) { isCircularPrime = false; break; } } if (isCircularPrime) { // 把所有旋转数加入循环素数集合 circularPrimes.addAll(rotations); // 标记这些旋转数为已处理,避免后续重复遍历 processedPrimes.addAll(rotations); } // 移除当前遍历的素数,清理原集合 iterator.remove(); } // 此时circularPrimes就是所有循环素数,primes剩下的是非循环素数
内容的提问来源于stack exchange,提问作者bisarch

