CopyOnWriteArraySet

About

CopyOnWriteArraySet is a thread-safe implementation of the Set interface. It is backed by a CopyOnWriteArrayList, meaning it creates a new copy of the underlying array every time a modification (add, remove, etc.) is made. This makes it particularly suitable for use cases where read operations vastly outnumber write operations, as it provides thread-safe iteration without the need for external synchronization.

Features

  1. Thread-Safe: All operations are thread-safe, providing consistent behavior in multi-threaded environments.

  2. No Duplicates: Like all Set implementations, it does not allow duplicate elements.

  3. Read-Mostly Optimization: Optimized for scenarios with frequent reads and infrequent writes, as reads do not block.

  4. Iterators Are Fail-Safe: Iterators operate on a snapshot of the set at the time they are created, meaning they will not throw ConcurrentModificationException.

  5. Array-Based Backing: Backed by a dynamically growing array (CopyOnWriteArrayList), making iteration very fast but write operations more costly.

Internal Working

Backing Structure

  • Internally uses a CopyOnWriteArrayList to store elements.

  • Each modification creates a new copy of the internal array, ensuring that readers see a consistent and unchanging view of the set.

Add Operation

  • When a new element is added, a new array is created, the element is added, and the old array is replaced.

  • Ensures no duplicates by checking if the element already exists.

Remove Operation

  • Similar to add, a new array is created without the specified element, and the old array is replaced.

Thread-Safety

  • Write operations (add, remove, etc.) are synchronized internally.

  • Read operations (contains, iterator, etc.) do not require synchronization and are very fast.

Snapshot Iterators

  • Iterators operate on a snapshot of the array taken at the time the iterator was created.

  • Modifications to the set during iteration will not affect the iterator.

Key Methods

Method

Description

add(E e)

Adds the specified element to the set if it is not already present.

remove(Object o)

Removes the specified element from the set if it exists.

contains(Object o)

Returns true if the set contains the specified element.

size()

Returns the number of elements in the set.

iterator()

Returns an iterator over the elements in the set.

clear()

Removes all elements from the set.

isEmpty()

Returns true if the set contains no elements.

toArray()

Returns an array containing all the elements in the set.

Big-O for Operations

Operation

Time Complexity

Add

O(n)

Remove

O(n)

Contains

O(n)

Iteration

O(n)

Limitations

  1. High Write Cost: Write operations (add, remove) are costly because a new array is created on every modification.

  2. Memory Usage: Creating a new array for every modification can increase memory usage, especially for large sets.

  3. No Real-Time Updates During Iteration: Iterators work on a snapshot and do not reflect modifications made after the iterator is created.

  4. Not Suitable for High-Write Scenarios: The overhead of copying the entire array makes it impractical for use cases with frequent write operations.

Real-World Usage

  1. Event Listener Management: Used for managing sets of listeners where additions or removals are rare but reads are frequent.

  2. Cache or Configuration: Useful for sets that change rarely but are frequently read in concurrent applications.

  3. Immutable Views: When a consistent snapshot of the data is required without blocking threads during iteration.

Examples

1. Basic Operations

import java.util.concurrent.CopyOnWriteArraySet;

public class CopyOnWriteArraySetExample {
    public static void main(String[] args) {
        CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();

        set.add(10);
        set.add(20);
        set.add(10); // Duplicate elements are ignored.

        System.out.println(set); // Output: [10, 20]

        // Check if an element exists
        System.out.println(set.contains(20)); // Output: true

        // Remove an element
        set.remove(10);
        System.out.println(set); // Output: [20]
    }
}

2. Fail-Safe Iteration

import java.util.concurrent.CopyOnWriteArraySet;

public class FailSafeIterationExample {
    public static void main(String[] args) {
        CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
        set.add("A");
        set.add("B");
        set.add("C");

        for (String s : set) {
            System.out.println(s); // Output: A, B, C (one per line)
            set.add("D"); // Modifying the set during iteration.
        }

        System.out.println(set); // Output: [A, B, C, D]
    }
}

3. Concurrent Access

import java.util.concurrent.CopyOnWriteArraySet;

public class ConcurrentAccessExample {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArraySet<Integer> set = new CopyOnWriteArraySet<>();

        Runnable writer = () -> {
            for (int i = 0; i < 5; i++) {
                set.add(i);
            }
        };

        Thread t1 = new Thread(writer);
        Thread t2 = new Thread(writer);

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println(set); // Output: [0, 1, 2, 3, 4]
    }
}

Last updated