
com.cedarsoftware.util.ConcurrentList Maven / Gradle / Ivy
package com.cedarsoftware.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.RandomAccess;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;
/**
* A thread-safe implementation of the {@link List} interface, designed for use in highly concurrent environments.
*
* The {@code ConcurrentList} can be used either as a standalone thread-safe list or as a wrapper to make an existing
* list thread-safe. It ensures thread safety without duplicating elements, making it suitable for applications
* requiring synchronized access to list data.
*
*
* Features
*
* - Standalone Mode: Use the no-argument constructor to create a new thread-safe {@code ConcurrentList}.
* - Wrapper Mode: Pass an existing {@link List} to the constructor to wrap it with thread-safe behavior.
* - Read-Only Iterators: The {@link #iterator()} and {@link #listIterator()} methods return a read-only
* snapshot of the list at the time of the call, ensuring safe iteration in concurrent environments.
* - Unsupported Operations: Due to the dynamic nature of concurrent edits, the
* {@link #subList(int, int)} method is not implemented.
*
*
* Thread Safety
*
* All public methods of {@code ConcurrentList} are thread-safe, ensuring that modifications and access
* operations can safely occur concurrently. However, thread safety depends on the correctness of the provided
* backing list in wrapper mode.
*
*
* Usage
* {@code
* // Standalone thread-safe list
* ConcurrentList standaloneList = new ConcurrentList<>();
* standaloneList.add("Hello");
* standaloneList.add("World");
*
* // Wrapping an existing list
* List existingList = new ArrayList<>();
* existingList.add("Java");
* existingList.add("Concurrency");
* ConcurrentList wrappedList = new ConcurrentList<>(existingList);
* }
*
* Performance Considerations
*
* The {@link #iterator()} and {@link #listIterator()} methods return read-only views created by copying
* the list contents, which ensures thread safety but may incur a performance cost for very large lists.
* Modifications to the list during iteration will not be reflected in the iterators.
*
*
* Additional Notes
*
* - {@code ConcurrentList} supports {@code null} elements if the underlying list does.
* - {@link #subList(int, int)} throws {@link UnsupportedOperationException}.
* - Implements {@link Serializable} and {@link RandomAccess} with a fair {@link ReentrantReadWriteLock}.
*
*
* @param The type of elements in this list
*
* @author John DeRegnaucourt ([email protected])
*
* Copyright (c) Cedar Software LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* License
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* @see List
*/
public final class ConcurrentList implements List, RandomAccess, Serializable {
private static final long serialVersionUID = 1L;
private final List list;
private final transient ReadWriteLock lock = new ReentrantReadWriteLock(true);
/**
* No-arg constructor to create an empty ConcurrentList, wrapping an ArrayList.
*/
public ConcurrentList() {
this.list = new ArrayList<>();
}
/**
* Initial capacity support
*/
public ConcurrentList(int size) {
this.list = new ArrayList<>(size);
}
/**
* Use this constructor to wrap a List (any kind of List) and make it a ConcurrentList.
* No duplicate of the List is created and the original list is operated on directly.
* @param list List instance to protect.
*/
public ConcurrentList(List list) {
if (list == null) {
throw new IllegalArgumentException("List cannot be null");
}
this.list = list;
}
// Immutable APIs
@Override
public boolean equals(Object other) { return withReadLock(() -> list.equals(other)); }
@Override
public int hashCode() { return withReadLock(list::hashCode); }
@Override
public String toString() { return withReadLock(list::toString); }
@Override
public int size() { return withReadLock(list::size); }
@Override
public boolean isEmpty() { return withReadLock(list::isEmpty); }
@Override
public boolean contains(Object o) { return withReadLock(() -> list.contains(o)); }
@Override
public boolean containsAll(Collection> c) { return withReadLock(() -> list.containsAll(c)); }
@Override
public E get(int index) { return withReadLock(() -> list.get(index)); }
@Override
public int indexOf(Object o) { return withReadLock(() -> list.indexOf(o)); }
@Override
public int lastIndexOf(Object o) { return withReadLock(() -> list.lastIndexOf(o)); }
@Override
public Iterator iterator() {
return withReadLock(() -> new ArrayList<>(list).iterator());
}
@Override
public Object[] toArray() { return withReadLock(list::toArray); }
@Override
public T[] toArray(T[] a) { return withReadLock(() -> list.toArray(a)); }
// Mutable APIs
@Override
public boolean add(E e) { return withWriteLock(() -> list.add(e)); }
@Override
public boolean addAll(Collection extends E> c) { return withWriteLock(() -> list.addAll(c)); }
@Override
public boolean addAll(int index, Collection extends E> c) { return withWriteLock(() -> list.addAll(index, c)); }
@Override
public void add(int index, E element) {
withWriteLockVoid(() -> list.add(index, element));
}
@Override
public E set(int index, E element) { return withWriteLock(() -> list.set(index, element)); }
@Override
public E remove(int index) { return withWriteLock(() -> list.remove(index)); }
@Override
public boolean remove(Object o) { return withWriteLock(() -> list.remove(o)); }
@Override
public boolean removeAll(Collection> c) { return withWriteLock(() -> list.removeAll(c)); }
@Override
public boolean retainAll(Collection> c) { return withWriteLock(() -> list.retainAll(c)); }
@Override
public void clear() {
withWriteLockVoid(() -> list.clear());
}
@Override
public ListIterator listIterator() {
return withReadLock(() -> new ArrayList<>(list).listIterator());
}
@Override
public ListIterator listIterator(int index) {
return withReadLock(() -> new ArrayList<>(list).listIterator(index));
}
@Override
public List subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException("subList not implemented for ConcurrentList");
}
private T withReadLock(Supplier operation) {
lock.readLock().lock();
try {
return operation.get();
} finally {
lock.readLock().unlock();
}
}
private void withReadLockVoid(Runnable operation) {
lock.readLock().lock();
try {
operation.run();
} catch (RuntimeException e) {
// swallow to ensure the lock is properly released
} finally {
lock.readLock().unlock();
}
}
private T withWriteLock(Supplier operation) {
lock.writeLock().lock();
try {
return operation.get();
} finally {
lock.writeLock().unlock();
}
}
private void withWriteLockVoid(Runnable operation) {
lock.writeLock().lock();
try {
operation.run();
} finally {
lock.writeLock().unlock();
}
}
}