Criteria | CopyOnWriteArraySet | HashSet |
---|---|---|
Thread Safety | Thread-safe. All mutative operations (add, remove, etc.) are implemented by making a fresh copy of the underlying array. | Not thread-safe. |
Performance | Read operations are fast and without locking, but writes are relatively slow due to copying. Not suitable for frequently modified sets. | Faster write operations, but not suitable for multi-threaded environments without proper synchronization. |
Memory Overhead | Higher overhead because a new array is created with every modification. | Lower overhead, as it uses a hash table for storage. |
Iterator Behavior | Iterators of this set will never throw ConcurrentModificationException. They reflect the state of the set at the time the iterator was created. | Iterators can throw ConcurrentModificationException if the set is modified while it’s being iterated over. |
Use-case | Useful in scenarios where read operations greatly outnumber write operations and thread-safety is required. | Commonly used for sets where quick access and no ordering is required, and that do not require synchronization. |
Null Elements | Allows null elements. | Allows null elements. |
Internal Implementation | Backed by a CopyOnWriteArrayList. | Backed by a HashMap where the set’s elements are the keys. |
Example: Difference Between CopyOnWriteArraySet and HashSet in Java
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
public class CopyOnWriteArraySetVsHashSetDemo {
public static void main(String[] args) {
// Using a HashSet
Set<String> hashSet = new HashSet<>();
hashSet.add("Element1");
hashSet.add("Element2");
// Using a CopyOnWriteArraySet
Set<String> cowArraySet = new CopyOnWriteArraySet<>();
cowArraySet.add("Element1");
cowArraySet.add("Element2");
// Modify HashSet while iterating (will throw exception)
try {
Iterator<String> it = hashSet.iterator();
while (it.hasNext()) {
System.out.println(it.next());
hashSet.add("NewElement");
}
} catch (Exception e) {
System.out.println("Caught exception: " + e.getClass().getSimpleName());
}
// Modify CopyOnWriteArraySet while iterating (will not throw exception)
Iterator<String> cowIt = cowArraySet.iterator();
while (cowIt.hasNext()) {
System.out.println(cowIt.next());
cowArraySet.add("NewElement");
}
System.out.println("CopyOnWriteArraySet size after modification: " + cowArraySet.size());
}
}
Output:
Element1 Element2 Caught exception: ConcurrentModificationException Element1 Element2 CopyOnWriteArraySet size after modification: 3
Explanation:
1. HashSet: It is not thread-safe. If you modify the set (add, remove elements) while iterating over it using an iterator, it will throw a ConcurrentModificationException.
2. CopyOnWriteArraySet: This is a thread-safe variant of HashSet. When the set is modified, a new copy of the underlying array is created. This means that iterators on a CopyOnWriteArraySet never see the changes and thus will never throw a ConcurrentModificationException. However, due to this behavior, it's not efficient for sets that are frequently modified.
3. In the provided example:
– We first create a HashSet and add two elements to it. When we try to modify the set (by adding a new element) while iterating over it, it throws a ConcurrentModificationException.
– Next, we create a CopyOnWriteArraySet and add two elements to it. We then modify the set (by adding a new element) while iterating over it. It doesn't throw any exception. But note that the size does not double like the list example because sets do not allow duplicate elements.
4. CopyOnWriteArraySet is useful in scenarios where write operations are infrequent and iteration is a more common operation, ensuring that there are no concurrent modification exceptions.