Java CopyOnWriteArraySet class

CopyOnWriteArraySet is a thread-safe Set in Java. It ensures safe concurrent access by creating a new copy of the internal array for each modification.

Java 10

The CopyOnWriteArraySet is part of the java.util.concurrent package and is a thread-safe variant of Set and is backed by a CopyOnWriteArrayList. It is quite helpful in scenarios where reads are more frequent than writes.

1. How does CopyOnWriteArraySet Work?

    Similar to CopyOnWriteArrayList, it is an immutable snapshot-style iterator method. Any modification to the collection creates a new copy of the underlying array.

    • When we call add(), it checks whether the element already exists in the underlying array:
      • If the element is not present, it creates a new copy of the array, adds the element to it, and replaces the old array with the new one.
      • If the element already exists, the set remains unchanged.
    • When we call remove(), it creates a new copy of the array, excluding the element to be removed. The old array is discarded, and the new array becomes the active data structure.
    • When we iterate, the iteration happens on a snapshot of the array that existed at the time the iterator was created.
      • Iterators are immutable, and no changes to the set during iteration affect the iterator.
      • No ConcurrentModificationException is thrown, even in concurrent environments.

    Since each modification creates a new array under the hood, the CopyOnWriteArraySet is not suitable for large datasets or frequent writes.

    2. Syntax

    Creating a CopyOnWriteArraySet is straightforward. Here, E represents the type of elements the set will hold.

    import java.util.concurrent.CopyOnWriteArraySet;
    
    CopyOnWriteArraySet<E> set = new CopyOnWriteArraySet<>()

    3. Common Methods

    The following methods are commonly used while interacting with the CopyOnWriteArraySet.

    MethodDescription
    add(E e)Adds the specified element if it is not already present.
    add(collection)Adds all of the elements in the specified collection to the set.
    remove(Object o)Removes the specified element, if present.
    contains(Object o)Checks if the set contains the specified element.
    size()Returns the number of elements in the set.
    iterator()Returns an iterator over the elements.
    clear()Removes all elements from the set.
    isEmpty()Returns true if the set contains no elements.

    4. CopyOnWriteArraySet Example

    Let us see a simple example of how to use the CopyOnWriteArraySet class.

    CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
    
    // Adding elements
    set.add("Apple");
    set.add("Banana");
    set.add("Cherry");
    set.add("Apple"); // Duplicate element, won't be added
    
    // Display the set
    System.out.println("Set: " + set); // Output: [Apple, Banana, Cherry]
    
    // Check for an element
    System.out.println("Contains 'Banana': " + set.contains("Banana")); // Output: true
    
    // Remove an element
    set.remove("Banana");
    System.out.println("Set after removal: " + set); // Output: [Apple, Cherry]

    5. No ConcurrentModificationException is thrown

    When an iterator is obtained from a CopyOnWriteArraySet, it operates on a snapshot of the array at the time the iterator was created. Since each modification creates a new array, different threads operating concurrently on the CopyOnWriteArraySet are essentially working on different arrays.

    As the underlying array might change due to concurrent modifications, the iterator’s view remains unchanged, preventing the ConcurrentModificationException. This mechanism guarantees thread safety without the need for explicit synchronization during iteration.

    CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();
    set.add(1);
    set.add(2);
    set.add(3);
    
    // Start a thread to modify the set
    new Thread(() -> {
      set.add(4);
      set.remove(1);
    }).start();
    
    // Iterate over the set
    for (int num : set) {
      System.out.println("Element: " + num);
      // Even if the set is modified, no ConcurrentModificationException is thrown
    }

    6. When to Use?

    Use CopyOnWriteArraySet in applications where set sizes generally stay small, read-only operations vastly outnumber mutative operations, and we need to prevent interference among threads during traversal.

    For example, we can use CopyOnWriteArraySet to maintain a list of event listeners where additions/removals are infrequent, but notifying listeners happens often.

    import java.util.concurrent.CopyOnWriteArraySet;
    
    public class ListenerManager {
    
        private final CopyOnWriteArraySet<String> listeners = new CopyOnWriteArraySet<>();
    
        public void addListener(String listener) {
            listeners.add(listener);
        }
    
        public void removeListener(String listener) {
            listeners.remove(listener);
        }
    
        public void notifyListeners(String event) {
            for (String listener : listeners) {
                System.out.println("Notifying " + listener + " of event: " + event);
            }
        }
    
        public static void main(String[] args) {
            ListenerManager manager = new ListenerManager();
            manager.addListener("Listener1");
            manager.addListener("Listener2");
    
            manager.notifyListeners("Event1");
        }
    }

    CopyOnWriteArraySet helps minimize programmer-controlled synchronization steps and moves control to inbuilt, well-tested APIs.

    Please note that for add operations, CopyOnWriteArraySet performs worse than HashSet due to the added step of creating a new backing array every time the set is updated. There is no performance overhead on read operations; both classes perform the same.

    7. Comparison with Other Collections

    Let’s compare the CopyOnWriteArraySet with other sets in the collection framework:

    FeatureCopyOnWriteArraySetHashSetConcurrentHashMap
    Thread-SafeYesNoYes
    Allows DuplicatesNoNoNo
    Performance on ReadsFastFastFast
    Performance on WritesExpensive (copy on write)FastModerate
    Consistent Snapshot During IterationYesNoNo

    8. Conclusion

    This Java Collection tutorial taught us to use the CopyOnWriteArraySet class, its constructors, methods, and usecases. We learned the internal working of CopyOnWriteArraySet and how it differs from CopyOnWriteArrayList.

    Drop me your questions in the comments.

    Happy Learning !!

    Weekly Newsletter

    Stay Up-to-Date with Our Weekly Updates. Right into Your Inbox.

    Comments

    Subscribe
    Notify of
    0 Comments
    Most Voted
    Newest Oldest
    Inline Feedbacks
    View all comments

    About Us

    HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

    It also shares the best practices, algorithms & solutions and frequently asked interview questions.