Design and implement a SnapshotSet<T> data structure that extends the functionality of a standard set by supporting snapshot-based iterators. The key requirement is that iterators capture a snapshot of the set at the time they are created, allowing them to iterate over the elements without being affected by subsequent modifications to the set.
Implement the SnapshotSet<T> class with the following methods:
add(T element): Adds the specified element to the set.remove(T element): Removes the specified element from the set.iterator(): Returns a snapshot-based iterator over the elements in the set.The iterator should have the following methods:
hasNext(): Returns true if the iterator has more elements to traverse.next(): Returns the next element in the iteration.`java SnapshotSet<Integer> set = new SnapshotSet<>(); set.add(1); set.add(2); set.add(3);
Iterator<Integer> iterator1 = set.iterator(); // Snapshot of the set at this point while (iterator1.hasNext()) { System.out.println(iterator1.next()); // Prints 1, 2, 3 }
set.add(4); set.remove(2);
Iterator<Integer> iterator2 = set.iterator(); // Snapshot of the set at this point while (iterator2.hasNext()) { System.out.println(iterator2.next()); // Prints 1, 3, 4 } `
add, remove, and iterator should be O(1) on average.`java import java.util.*;
public class SnapshotSet<T> { private static class Node { T value; Node next;
Node(T value) {
this.value = value;
}
}
private Map<T, Node> map;
private Node head;
private int version;
public SnapshotSet() {
map = new HashMap<>();
head = new Node(null);
version = 0;
}
public void add(T element) {
if (!map.containsKey(element)) {
Node newNode = new Node(element);
newNode.next = head.next;
head.next = newNode;
map.put(element, newNode);
version++;
}
}
public void remove(T element) {
if (map.containsKey(element)) {
Node nodeToRemove = map.get(element);
Node current = head;
while (current.next != nodeToRemove) {
current = current.next;
}
current.next = nodeToRemove.next;
map.remove(element);
version++;
}
}
public Iterator<T> iterator() {
return new Iterator<T>() {
private Node current = head.next;
private int iteratorVersion = version;
@Override
public boolean hasNext() {
return current != null;
}
@Override
public T next() {
if (iteratorVersion != version) {
throw new ConcurrentModificationException();
}
T value = current.value;
current = current.next;
return value;
}
};
}
} `
This solution uses a combination of a hash map and a linked list to store the elements. The hash map allows for efficient lookups, while the linked list maintains the order of insertion. The iterator captures a snapshot of the set by storing the current version number and iterating over the linked list. If the set is modified while iterating, a ConcurrentModificationException is thrown.