求问:支持无重复元素且迭代时可添加元素的数据结构及Set迭代添加方案
嘿,这个问题问得很实际,我来给你详细拆解一下~
一、有没有原生支持的无重复元素且迭代时可添加的数据结构?
有的,在Java的并发集合体系里就有现成的实现:
ConcurrentSkipListSet:基于跳表实现的有序、无重复元素的并发集合。它的迭代器是弱一致性的——迭代过程中添加元素不会抛出
ConcurrentModificationException,不过迭代器不会立即反映出新增的元素(因为迭代器基于当前集合的一个视图,后续修改不会影响这个视图)。适合需要有序特性、且有并发修改需求的场景。CopyOnWriteArraySet:基于CopyOnWriteArrayList实现,底层是写时复制机制。当你添加元素时,它会复制一份新的数组存储元素,迭代器使用的是原数组的快照,所以迭代过程中添加元素完全不会干扰迭代,也不会抛出异常,但同样,迭代过程中看不到新添加的元素,直到你开启新的迭代。适合读多写少的场景。
二、普通Set(如HashSet/TreeSet)迭代时添加元素的解决办法
普通非并发Set迭代时直接添加元素会触发「快速失败」机制,抛出异常,这里有两种常用的解决思路:
1. 用临时集合缓存待添加元素,迭代完成后批量添加
这是最常用的方案,既不会破坏原集合的迭代逻辑,也能避免异常:
import java.util.HashSet; import java.util.Set; public class SetAddExample { public static void main(String[] args) { Set<String> mySet = new HashSet<>(); mySet.add("apple"); mySet.add("banana"); mySet.add("cherry"); // 临时集合存储要添加的元素 Set<String> elementsToAdd = new HashSet<>(); // 迭代原集合 for (String fruit : mySet) { System.out.println("当前元素: " + fruit); // 满足条件时添加到临时集合 if (fruit.equals("banana")) { elementsToAdd.add("date"); elementsToAdd.add("elderberry"); } } // 迭代完成后批量添加 mySet.addAll(elementsToAdd); System.out.println("最终集合: " + mySet); } }
2. 切换为并发Set实现
如果你的场景允许使用并发集合,可以直接替换为前面提到的ConcurrentSkipListSet或CopyOnWriteArraySet,这样迭代过程中就能直接添加元素而不抛出异常。比如:
import java.util.concurrent.ConcurrentSkipListSet; public class ConcurrentSetExample { public static void main(String[] args) { ConcurrentSkipListSet<String> mySet = new ConcurrentSkipListSet<>(); mySet.add("cat"); mySet.add("dog"); mySet.add("elephant"); // 迭代时直接添加元素 for (String animal : mySet) { System.out.println("当前元素: " + animal); if (animal.equals("dog")) { mySet.add("fox"); mySet.add("giraffe"); } } System.out.println("最终集合: " + mySet); } }
补充:为什么普通Set迭代时不能直接添加?
普通HashSet、TreeSet的迭代器是**快速失败(fail-fast)**设计,迭代过程中会维护一个modCount(修改次数)变量,每次集合被修改(add/remove等)modCount都会递增。迭代器每次next()时都会检查modCount是否和自己初始化时的expectedModCount一致,如果不一致就抛出ConcurrentModificationException——目的是及时发现并发修改导致的不一致问题,避免后续出现不可预期的行为。
内容的提问来源于stack exchange,提问作者V. M.

