Skip to content

Commit

Permalink
- Added the SealableXXX collection classes.
Browse files Browse the repository at this point in the history
- Strengthened the implementation of ConcurrentList and ConcurrentHashSet.
  • Loading branch information
jdereg committed Apr 29, 2024
1 parent 8fb9827 commit f175031
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 336 deletions.
5 changes: 3 additions & 2 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
* Added `SealableNavigableSet` similar to SealableList but with `NavigableSet` nature.
* Added `SealableNavigableMap` similar to SealableList but with `NavigableMap` nature.
* Updated `ConcurrentList` to support wrapping any `List` and making it thread-safe, including all view APIs: `iterator(),` `listIterator(),` `listIterator(index).` The no-arg constructor creates a `ConcurrentList` ready-to-go. The constructor that takes a `List` parameter constructor wraps the passed in list and makes it thread-safe.
* Renamed `ConcurrentHashSet` to `ConcurrentSet.`
* 2.8.0
* Added `ClassUtilities.doesOneWrapTheOther()` API so that it is easy to test if one class is wrapping the other.
* Added `StringBuilder` and `StringBuffer` to `Strings` to the `Converter.` Eliminates special cases for `.toString()` calls where generalized `convert(src, type)` is being used.
* 2.7.0
* Added `ConcurrentHashList,` which implements a thread-safe `List.` Provides all API support except for `listIterator(),` however, it implements `iterator()` which returns an iterator to a snapshot copy of the `List.`
* Added `ConcurrentHashSet,` a true `Set` which is a bit easier to use than `ConcurrentSkipListSet,` which as a `NaviableSet` and `SortedSet,` requires each element to be `Comparable.`
* Added `ConcurrentList,` which implements a thread-safe `List.` Provides all API support except for `listIterator(),` however, it implements `iterator()` which returns an iterator to a snapshot copy of the `List.`
* Added `ConcurrentHashSet,` a true `Set` which is a bit easier to use than `ConcurrentSkipListSet,` which as a `NavigableSet` and `SortedSet,` requires each element to be `Comparable.`
* Performance improvement: On `LRUCache,` removed unnecessary `Collections.SynchronizedMap` surrounding the internal `LinkedHashMap` as the concurrent protection offered by `ReentrantReadWriteLock` is all that is needed.
* 2.6.0
* Performance improvement: `Converter` instance creation is faster due to the code no longer copying the static default table. Overrides are kept in separate variable.
Expand Down
259 changes: 47 additions & 212 deletions src/main/java/com/cedarsoftware/util/ConcurrentList.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.ListIterator;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;

/**
* ConcurrentList provides a List and List wrapper that is thread-safe, usable in highly concurrent
Expand Down Expand Up @@ -38,250 +39,84 @@
*/
public class ConcurrentList<E> implements List<E> {
private final List<E> list;
private final ReadWriteLock lock;
private final transient ReadWriteLock lock = new ReentrantReadWriteLock();

/**
* Use this no-arg constructor to create a ConcurrentList.
* No-arg constructor to create an empty ConcurrentList, wrapping an ArrayList.
*/
public ConcurrentList() {
lock = new ReentrantReadWriteLock();
this.list = new ArrayList<>();
}

/**
* 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<E> list) {
if (list == null) {
throw new IllegalArgumentException("list cannot be null");
throw new IllegalArgumentException("List cannot be null");
}
lock = new ReentrantReadWriteLock();
this.list = list;
}

public int size() {
lock.readLock().lock();
try {
return list.size();
} finally {
lock.readLock().unlock();
}
}

public boolean isEmpty() {
lock.readLock().lock();
try {
return list.isEmpty();
} finally {
lock.readLock().unlock();
}
}

public boolean equals(Object obj) {
lock.readLock().lock();
try {
return list.equals(obj);
} finally {
lock.readLock().unlock();
}
}

public int hashCode() {
lock.readLock().lock();
try {
return list.hashCode();
} finally {
lock.readLock().unlock();
}
}

public String toString() {
lock.readLock().lock();
try {
return list.toString();
} finally {
lock.readLock().unlock();
}
}

public boolean contains(Object o) {
lock.readLock().lock();
try {
return list.contains(o);
} finally {
lock.readLock().unlock();
}
}

public Iterator<E> iterator() {
lock.readLock().lock();
try {
return new ArrayList<>(list).iterator();
} finally {
lock.readLock().unlock();
}
}

public Object[] toArray() {
lock.readLock().lock();
try {
return list.toArray();
} finally {
lock.readLock().unlock();
}
}

public <T> T[] toArray(T[] a) {
lock.readLock().lock();
try {
return list.toArray(a);
} finally {
lock.readLock().unlock();
}
}

public boolean add(E e) {
lock.writeLock().lock();
try {
return list.add(e);
} finally {
lock.writeLock().unlock();
}
}

public boolean remove(Object o) {
lock.writeLock().lock();
try {
return list.remove(o);
} finally {
lock.writeLock().unlock();
}
}

public boolean containsAll(Collection<?> c) {
lock.readLock().lock();
try {
return new HashSet<>(list).containsAll(c);
} finally {
lock.readLock().unlock();
}
}

public boolean addAll(Collection<? extends E> c) {
lock.writeLock().lock();
try {
return list.addAll(c);
} finally {
lock.writeLock().unlock();
}
}

public boolean addAll(int index, Collection<? extends E> c) {
lock.writeLock().lock();
try {
return list.addAll(index, c);
} finally {
lock.writeLock().unlock();
}
}

public boolean removeAll(Collection<?> c) {
lock.writeLock().lock();
try {
return list.removeAll(c);
} finally {
lock.writeLock().unlock();
}
}

public boolean retainAll(Collection<?> c) {
lock.writeLock().lock();
try {
return list.retainAll(c);
} finally {
lock.writeLock().unlock();
}
}

// Immutable APIs
public boolean equals(Object other) { return readOperation(() -> list.equals(other)); }
public int hashCode() { return readOperation(list::hashCode); }
public String toString() { return readOperation(list::toString); }
public int size() { return readOperation(list::size); }
public boolean isEmpty() { return readOperation(list::isEmpty); }
public boolean contains(Object o) { return readOperation(() -> list.contains(o)); }
public boolean containsAll(Collection<?> c) { return readOperation(() -> new HashSet<>(list).containsAll(c)); }
public E get(int index) { return readOperation(() -> list.get(index)); }
public int indexOf(Object o) { return readOperation(() -> list.indexOf(o)); }
public int lastIndexOf(Object o) { return readOperation(() -> list.lastIndexOf(o)); }
public Iterator<E> iterator() { return readOperation(() -> new ArrayList<>(list).iterator()); }
public Object[] toArray() { return readOperation(list::toArray); }
public <T> T[] toArray(T[] a) { return readOperation(() -> list.toArray(a)); }

// Mutable APIs
public boolean add(E e) { return writeOperation(() -> list.add(e)); }
public boolean addAll(Collection<? extends E> c) { return writeOperation(() -> list.addAll(c)); }
public boolean addAll(int index, Collection<? extends E> c) { return writeOperation(() -> list.addAll(index, c)); }
public void add(int index, E element) {
writeOperation(() -> {
list.add(index, element);
return null;
});
}
public E set(int index, E element) { return writeOperation(() -> list.set(index, element)); }
public E remove(int index) { return writeOperation(() -> list.remove(index)); }
public boolean remove(Object o) { return writeOperation(() -> list.remove(o)); }
public boolean removeAll(Collection<?> c) { return writeOperation(() -> list.removeAll(c)); }
public boolean retainAll(Collection<?> c) { return writeOperation(() -> list.retainAll(c)); }
public void clear() {
lock.writeLock().lock();
try {
writeOperation(() -> {
list.clear();
} finally {
lock.writeLock().unlock();
}
return null; // To comply with the Supplier<T> return type
});
}
public ListIterator<E> listIterator() { return readOperation(() -> new ArrayList<>(list).listIterator()); }

public E get(int index) {
// Unsupported operations
public ListIterator<E> listIterator(int index) { throw new UnsupportedOperationException("listIterator(index) not implemented for ConcurrentList"); }
public List<E> subList(int fromIndex, int toIndex) { throw new UnsupportedOperationException("subList not implemented for ConcurrentList"); }

private <T> T readOperation(Supplier<T> operation) {
lock.readLock().lock();
try {
return list.get(index);
return operation.get();
} finally {
lock.readLock().unlock();
}
}

public E set(int index, E element) {
private <T> T writeOperation(Supplier<T> operation) {
lock.writeLock().lock();
try {
return list.set(index, element);
return operation.get();
} finally {
lock.writeLock().unlock();
}
}

public void add(int index, E element) {
lock.writeLock().lock();
try {
list.add(index, element);
} finally {
lock.writeLock().unlock();
}
}

public E remove(int index) {
lock.writeLock().lock();
try {
return list.remove(index);
} finally {
lock.writeLock().unlock();
}
}

public int indexOf(Object o) {
lock.readLock().lock();
try {
return list.indexOf(o);
} finally {
lock.readLock().unlock();
}
}

public int lastIndexOf(Object o) {
lock.readLock().lock();
try {
return list.lastIndexOf(o);
} finally {
lock.readLock().unlock();
}
}

public List<E> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException("subList not implemented for ConcurrentList");
}

public ListIterator<E> listIterator() {
lock.readLock().lock();
try {
return new ArrayList<E>(list).listIterator();
} finally {
lock.readLock().unlock();
}
}

public ListIterator<E> listIterator(int index) {
throw new UnsupportedOperationException("listIterator(index) not implemented for ConcurrentList");
}
}
11 changes: 10 additions & 1 deletion src/main/java/com/cedarsoftware/util/ConcurrentSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,17 @@
* limitations under the License.
*/
public class ConcurrentSet<T> implements Set<T> {
private final Set<T> set = ConcurrentHashMap.newKeySet();
private final Set<T> set;

public ConcurrentSet() {
set = ConcurrentHashMap.newKeySet();
}

public ConcurrentSet(Collection<T> col) {
set = ConcurrentHashMap.newKeySet(col.size());
set.addAll(col);
}

// Immutable APIs
public boolean equals(Object other) { return set.equals(other); }
public int hashCode() { return set.hashCode(); }
Expand Down
Loading

0 comments on commit f175031

Please sign in to comment.