javolution.util.FastCollection Maven / Gradle / Ivy
/*
* Javolution - Java(TM) Solution for Real-Time and Embedded Systems
* Copyright (C) 2012 - Javolution (http://javolution.org/)
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software is
* freely granted, provided that this notice is preserved.
*/
package javolution.util;
import static javolution.lang.Realtime.Limit.CONSTANT;
import static javolution.lang.Realtime.Limit.LINEAR;
import static javolution.lang.Realtime.Limit.N_SQUARE;
import java.io.IOException;
import java.io.Serializable;
import java.util.Collection;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import javolution.lang.Immutable;
import javolution.lang.Parallelizable;
import javolution.lang.Realtime;
import javolution.text.Cursor;
import javolution.text.DefaultTextFormat;
import javolution.text.TextContext;
import javolution.text.TextFormat;
import javolution.util.function.Consumer;
import javolution.util.function.Equalities;
import javolution.util.function.Equality;
import javolution.util.function.Function;
import javolution.util.function.Predicate;
import javolution.util.function.Reducer;
import javolution.util.function.Reducers;
import javolution.util.internal.collection.AtomicCollectionImpl;
import javolution.util.internal.collection.DistinctCollectionImpl;
import javolution.util.internal.collection.FilteredCollectionImpl;
import javolution.util.internal.collection.MappedCollectionImpl;
import javolution.util.internal.collection.ParallelCollectionImpl;
import javolution.util.internal.collection.ReversedCollectionImpl;
import javolution.util.internal.collection.SequentialCollectionImpl;
import javolution.util.internal.collection.SharedCollectionImpl;
import javolution.util.internal.collection.SortedCollectionImpl;
import javolution.util.internal.collection.UnmodifiableCollectionImpl;
import javolution.util.service.CollectionService;
/**
* A closure-based collection supporting numerous views which can be chained.
*
* - {@link #atomic} - Thread-safe view for which all reads are mutex-free
* and collection updates (including {@link #addAll addAll}, {@link #removeIf removeIf}} are atomic.
* - {@link #shared} - Thread-safe view using allowing concurrent reads based
* on mutex (
* readers-writer locks).
* - {@link #parallel} - A view allowing parallel processing including {@link #update updates}.
* - {@link #sequential} - View disallowing parallel processing.
* - {@link #unmodifiable} - View which does not allow any modification.
* - {@link #filtered filtered(filter)} - View exposing only the elements matching the specified filter.
* - {@link #mapped mapped(function)} - View exposing elements through the specified mapping function.
* - {@link #sorted sorted(comparator)} - View exposing elements sorted according to their natural order
* of using a specified comparator.
* - {@link #reversed} - View exposing elements in the reverse iterative order.
* - {@link #distinct} - View exposing each element only once.
*
*
* Unmodifiable collections are not always immutable. An {@link javolution.lang.Immutable immutable}.
* reference (or const reference) can only be {@link #toImmutable() obtained} when the originator
* guarantees that the collection source will not be modified even by himself
* (the value of the immutable reference being an {@link #unmodifiable unmodifiable} collection).
* [code]
* Immutable> winners
* = new FastTable().addAll("John Deuff", "Otto Graf", "Sim Kamil").toImmutable();
* // Immutability is guaranteed, no reference left on the collection source.
* [/code]
*
* Atomic collections use Copy-On-Write
* optimizations in order to provide mutex-free read access. Only writes operations are mutually
* exclusive. Collections can be optimized to not require the full copy during write operations
* (e.g. immutable parts don't need to be copied). Still, when multiple updates are performed,
* it is beneficial to group them into one single {@link #update update} operation.
* [code]
* FastTable tokens = ...
* ...
* // Replace null with "" in tokens. If tokens is atomic the update is atomic.
* // If tokens is parallel, the update is also performed concurrently !
* tokens.update(new Consumer>() {
* public void accept(List view) {
* for (int i=0, n = view.size(); i < n; i++)
* if (view.get(i) == null) view.set(i, "");
* }
* }
* });[/code]
* The same code using closure (Java 8).
* [code]
* tokens.update((List view) -> {
* for (int i = 0, n = view.size(); i < n; i++) {
* if (view.get(i) == null) view.set(i, "");
* }
* });[/code]
*
* Views are similar to
* Java 8 streams except that views are themselves collections (virtual collections)
* and actions on a view can impact the original collection. Collection views are nothing "new"
* since they already existed in the original java.util collection classes (e.g. List.subList(...),
* Map.keySet(), Map.values()). Javolution extends to this concept and allows views to be chained
* which addresses the concern of class proliferation.
* [code]
* FastTable names = new FastTable().addAll("Oscar Thon", "Eva Poret", "Paul Auchon");
* boolean found = names.comparator(Equalities.LEXICAL_CASE_INSENSITIVE).contains("LUC SURIEUX");
* names.subTable(0, n).clear(); // Removes the n first names (see java.util.List.subList).
* names.distinct().add("Guy Liguili"); // Adds "Guy Liguili" only if not already present.
* names.filtered(isLong).clear(); // Removes all the persons with long names.
* names.filtered(isLong).parallel().clear(); // Same as above but performed concurrently !
* ...
* Predicate isLong = new Predicate() {
* public boolean test(CharSequence csq) {
* return csq.length() > 16;
* }
* });[/code]
*
* Views can of course be used to perform "stream" oriented filter-map-reduce operations with the same benefits:
* Parallelism support, excellent memory characteristics (no caching and cost nothing to create), etc.
* [code]
* String anyLongName = names.filtered(isLong).any(String.class); // Returns any long name.
* int nbrChars = names.mapped(toLength).reduce(Reducers.sum()); // Returns the total number of characters.
* int maxLength = names.mapped(toLength).parallel().max(); // Finds the longest name in parallel.
* ...
* Function toLength = new Function() {
* public Integer apply(CharSequence csq) {
* return csq.length();
* }
* });
*
* // JDK Class.getEnclosingMethod using Javolution's views and Java 8 (to be compared with the current 20 lines implementation !).
* Method matching = new FastTable().addAll(enclosingInfo.getEnclosingClass().getDeclaredMethods())
* .filtered(m -> Equalities.STANDARD.areEqual(m.getName(), enclosingInfo.getName())
* .filtered(m -> Equalities.ARRAY.areEqual(m.getParameterTypes(), parameterClasses))
* .filtered(m -> Equalities.STANDARD.areEqual(m.getReturnType(), returnType))
* .any(Method.class);
* if (matching == null) throw new InternalError("Enclosing method not found");
* return matching;[/code]
*
* If a collection (or a map) is shared, derived views are also thread-safe.
* Similarly, if a collection is {@link #parallel parallel}, closure-based iterations
* on derived views are performed concurrently.
* [code]
* FastMap tasks = ...
* ...
* tasks.values().parallel().forEach(new Consumer() { // Executes task concurrently.
* public void accept(Runnable task) {
* task.run();
* }
* });[/code]
* With Java 8, closures are greatly simplified using lambda expressions.
* [code]
* tasks.values().parallel().forEach(task -> task.run()); // Same as above.
* names.sorted().reversed().forEach(str -> System.out.println(str)); // Prints names in reverse alphabetical order.
* [/code]
*
* @author Jean-Marie Dautelle
* @version 6.0, July 21, 2013
*/
@Realtime
@DefaultTextFormat(FastCollection.Format.class)
public abstract class FastCollection implements Collection, Serializable {
private static final long serialVersionUID = 0x600L; // Version.
/**
* Default constructor.
*/
protected FastCollection() {}
////////////////////////////////////////////////////////////////////////////
// Views.
//
/**
* Returns an atomic view over this collection. All operations that write
* or access multiple elements in the collection (such as addAll(),
* retainAll()) are atomic.
* Iterators on atomic collections are thread-safe
* (no {@link ConcurrentModificationException} possible).
*/
@Parallelizable(mutexFree = true, comment = "Except for write operations, all read operations are mutex-free.")
public FastCollection atomic() {
return new AtomicCollectionImpl(service());
}
/**
* Returns a thread-safe view over this collection. The shared view
* allows for concurrent read as long as there is no writer.
* The default implementation is based on
* readers-writers locks giving priority to writers.
* Iterators on shared collections are thread-safe
* (no {@link ConcurrentModificationException} possible).
*/
@Parallelizable(mutexFree = false, comment = "Use multiple-readers/single-writer lock.")
public FastCollection shared() {
return new SharedCollectionImpl(service());
}
/**
* Returns a parallel collection. Parallel collections affect
* only closure-based operations, all others operations behaving the
* same. Parallel actions are performed concurrently using Javolution
* {@link javolution.context.ConcurrentContext ConcurrentContext}.
* The number of parallel views is derived from the context
* {@link javolution.context.ConcurrentContext#getConcurrency()
* concurrency} ({@code number of parallel views = concurrency + 1}).
* Parallel views do not require this collection to be thread-safe
* (internal synchronization).
*
* @see #perform(Consumer)
* @see #update(Consumer)
* @see #forEach(Consumer)
* @see #removeIf(Predicate)
* @see #reduce(Reducer)
*/
public FastCollection parallel() {
return new ParallelCollectionImpl(service());
}
/**
* Returns a sequential view of this collection. Using this view,
* all closure-based iterations are performed sequentially.
*/
public FastCollection sequential() {
return new SequentialCollectionImpl(service());
}
/**
* Returns an unmodifiable view over this collection. Any attempt to
* modify the collection through this view will result into
* a {@link java.lang.UnsupportedOperationException} being raised.
*/
public FastCollection unmodifiable() {
return new UnmodifiableCollectionImpl(service());
}
/**
* Returns a view exposing only the elements matching the specified
* filter. Adding elements not matching the specified filter has
* no effect. If this collection is initially empty, using a filtered
* view to add new elements ensure that this collection has only elements
* satisfying the filter predicate.
*/
public FastCollection filtered(Predicate super E> filter) {
return new FilteredCollectionImpl(service(), filter);
}
/**
* Returns a view exposing elements through the specified mapping function.
* The returned view does not allow new elements to be added.
*/
public FastCollection mapped(
Function super E, ? extends R> function) {
return new MappedCollectionImpl(service(), function);
}
/**
* Returns a view exposing elements sorted according to the
* collection {@link #comparator() order}.
*/
public FastCollection sorted() {
return new SortedCollectionImpl(service(), comparator());
}
/**
* Returns a view exposing elements sorted according to the specified
* comparator.
*/
public FastCollection sorted(Comparator super E> cmp) {
return new SortedCollectionImpl(service(), cmp);
}
/**
* Returns a view exposing elements in reverse iterative order.
*/
public FastCollection reversed() {
return new ReversedCollectionImpl(service());
}
/**
* Returns a view exposing only distinct elements (it does not iterate twice
* over the {@link #comparator() same} elements). Adding elements already
* in the collection through this view has no effect. If this collection is
* initially empty, using a distinct view to add new elements ensures that
* this collection has no duplicate.
*/
public FastCollection distinct() {
return new DistinctCollectionImpl(service());
}
////////////////////////////////////////////////////////////////////////////
// Closure operations.
//
/**
* Executes the specified read-only action on this collection.
* That logic may be performed concurrently on sub-collections
* if this collection is {@link #parallel() parallel}.
*
* @param action the read-only action.
* @throws UnsupportedOperationException if the action tries to update
* this collection and this collection is thread-safe.
* @throws ClassCastException if the action type is not compatible with
* this collection (e.g. action on set and this is a list).
* @see #update(Consumer)
*/
@SuppressWarnings("unchecked")
@Realtime(limit = LINEAR)
public void perform(Consumer extends Collection> action) {
service().perform((Consumer>) action, service());
}
/**
* Executes the specified update action on this collection.
* That logic may be performed concurrently on sub-collections
* if this collection is {@link #parallel() parallel}.
* For {@link #atomic() atomic} collections the update is atomic
* (either concurrent readers see the full result of the action or
* nothing).
*
* @param action the update action.
* @throws ClassCastException if the action type is not compatible with
* this collection (e.g. action on set and this is a list).
* @see #perform(Consumer)
*/
@SuppressWarnings("unchecked")
@Realtime(limit = LINEAR)
public void update(Consumer extends Collection> action) {
service().update((Consumer>) action, service());
}
/**
* Iterates over all this collection elements applying the specified
* consumer (convenience method). Iterations are performed concurrently
* if the collection is {@link #parallel() parallel}.
*
* @param consumer the functional consumer applied to the collection elements.
*/
@Realtime(limit = LINEAR)
public void forEach(final Consumer super E> consumer) {
perform(new Consumer>() {
public void accept(Collection view) {
Iterator it = view.iterator();
while (it.hasNext()) {
consumer.accept(it.next());
}
}
});
}
/**
* Removes from this collection all the elements matching the specified
* functional predicate (convenience method). Removals are performed
* concurrently if this collection is {@link #parallel() parallel} and
* atomically if this collection is {@link #atomic() atomic}.
*
* @param filter a predicate returning {@code true} for elements to be removed.
* @return {@code true} if at least one element has been removed;
* {@code false} otherwise.
*/
@Realtime(limit = LINEAR)
public boolean removeIf(final Predicate super E> filter) {
final boolean[] removed = new boolean[1];
update(new Consumer>() {
public void accept(Collection view) {
Iterator it = view.iterator();
while (it.hasNext()) {
if (filter.test(it.next())) {
it.remove(); // Ok mutable iteration.
removed[0] = true;
}
}
}
});
return removed[0];
}
/**
* Performs a reduction of the elements of this collection using the
* specified reducer. This may not involve iterating over all the
* collection elements, for example the reducers: {@link Reducers#any},
* {@link Reducers#and} and {@link Reducers#or} may stop iterating
* early. Reduction is performed concurrently if this collection is
* {@link #parallel() parallel}.
*
* @param reducer the collection reducer.
* @return the reduction result.
*/
@Realtime(limit = LINEAR)
public E reduce(Reducer reducer) {
perform(reducer);
return reducer.get();
}
////////////////////////////////////////////////////////////////////////////
// Collection operations.
//
/** Adds the specified element to this collection */
@Override
@Realtime(limit = LINEAR, comment = "May have to search the whole collection (e.g. distinct view).")
public boolean add(E element) {
return service().add(element);
}
/** Indicates if this collection is empty. */
@Override
@Realtime(limit = LINEAR, comment = "May actually iterates the whole collection (e.g. filtered view).")
public boolean isEmpty() {
return iterator().hasNext();
}
/** Returns the size of this collection. */
@Override
@Realtime(limit = LINEAR, comment = "Potentially counts the elements.")
public int size() {
return service().size();
}
/** Removes all elements from this collection. */
@Override
@Realtime(limit = LINEAR, comment = "Potentially removes elements one at a time.")
public void clear() {
service().clear();
}
/** Indicates if this collection contains the specified element. */
@Override
@Realtime(limit = LINEAR, comment = "Potentially searches the whole collection.")
public boolean contains(Object searched) {
return service().contains(searched);
}
/** Removes the specified element from this collection. */
@Override
@Realtime(limit = LINEAR, comment = "Potentially searches the whole collection.")
public boolean remove(Object searched) {
return service().remove(searched);
}
/**
* Returns an iterator over this collection elements. For
* shared/atomic collections the iterator is immune to
* concurrent modifications. In other words the elements iterated over
* may or may not reflect the current state of the collection.
*/
@Override
@Realtime(limit = N_SQUARE, comment = "Construction of the iterator may require sorting the elements (e.g. sorted view)")
public Iterator iterator() {
return service().iterator();
}
/** Adds all the specified elements to this collection. */
@Override
@Realtime(limit = LINEAR)
public boolean addAll(final Collection extends E> that) {
return service().addAll(that);
}
/** Indicates if this collection contains all the specified elements. */
@Override
@Realtime(limit = N_SQUARE)
public boolean containsAll(Collection> that) {
return service().containsAll(that);
}
/** Removes all the specified element from this collection. */
@Override
@Realtime(limit = N_SQUARE)
public boolean removeAll(final Collection> that) {
return service().removeAll(that);
}
/** Removes all the elements except those in the specified collection. */
@Override
@Realtime(limit = N_SQUARE)
public boolean retainAll(final Collection> that) {
return service().retainAll(that);
}
/** Returns an array holding this collection elements. */
@Override
@Realtime(limit = LINEAR)
public Object[] toArray() {
return service().toArray();
}
/** Returns the specified array holding this collection elements if
* enough capacity. */
@Override
@Realtime(limit = LINEAR)
public T[] toArray(final T[] array) {
return service().toArray(array);
}
////////////////////////////////////////////////////////////////////////////
// Misc.
//
/**
* Returns any non-null element of the specified type (convenience method).
*
* @param type the element type searched for.
* @return {@code reduce(Reducers.any(type))}
* @see Reducers#any
*/
@SuppressWarnings("unchecked")
@Realtime(limit = LINEAR)
public T any(Class type) {
return (T) reduce((Reducer) Reducers.any(type));
}
/**
* Returns the smallest element of this collection using this
* collection {@link #comparator() comparator} (convenience method).
* Returns {@code null} if this collection is empty.
*
* @return {@code reduce(Reducers.min(comparator()))}
* @see Reducers#min
*/
@Realtime(limit = LINEAR)
public E min() {
return reduce(Reducers.min(comparator()));
}
/**
* Returns the largest element of this collection using this
* collection {@link #comparator() comparator} (convenience method).
* Returns {@code null} if this collection is empty.
*
* @return {@code reduce(Reducers.max(comparator()))}
* @see Reducers#max
*/
@Realtime(limit = LINEAR)
public E max() {
return reduce(Reducers.max(comparator()));
}
/**
* Returns this collection with the specified element added.
*
* @param elements the elements to be added.
* @return {@code this}
*/
@Realtime(limit = LINEAR)
public FastCollection addAll(E... elements) {
for (E e : elements) {
add(e);
}
return this;
}
/**
* Returns this collection with the specified collection's elements added
* in sequence.
*/
@Realtime(limit = LINEAR)
public FastCollection addAll(FastCollection extends E> that) {
addAll((Collection extends E>) that);
return this;
}
/**
* Returns the comparator uses by this collection for equality and/or
* ordering if this collection is sorted.
*/
@Realtime(limit = CONSTANT)
public Equality super E> comparator() {
return service().comparator();
}
/**
* Returns an immutable reference over this collection. The immutable
* value is an {@link #unmodifiable() unmodifiable} view of this collection.
* The caller must guarantees that the original collection is never going
* to be updated (e.g. there is no reference left of the original collection).
*/
@Realtime(limit = CONSTANT)
public > Immutable toImmutable() {
return new Immutable() {
@SuppressWarnings("unchecked")
final T value = (T) unmodifiable();
@Override
public T value() {
return value;
}
};
}
/**
* Compares the specified object with this collection for equality.
* This method follows the {@link Collection#equals(Object)} specification
* if this collection {@link #comparator comparator} is
* {@link Equalities#STANDARD} (default). Otherwise, only collections
* using the same comparator can be considered equals.
*
* @param obj the object to be compared for equality with this collection
* @return true
if both collections are considered equals;
* false
otherwise.
*/
@Override
@Realtime(limit = LINEAR)
public boolean equals(Object obj) {
return service().equals(obj);
}
/**
* Returns the hash code of this collection.
* This method follows the {@link Collection#hashCode()} specification
* if this collection {@link #comparator comparator} is
* {@link Equalities#STANDARD}.
*
* @return this collection hash code.
*/
@Override
@Realtime(limit = LINEAR)
public int hashCode() {
return service().hashCode();
}
/** Returns the string representation of this collection using its
* default {@link TextFormat format}.
*/
@Override
@Realtime(limit = LINEAR)
public String toString() {
return TextContext.getFormat(FastCollection.class).format(this);
}
/**
* Returns the service implementation of this collection (for sub-classes).
*/
protected abstract CollectionService service();
/**
* Returns the service implementation of any fast collection
* (for sub-classes).
*/
protected static CollectionService serviceOf(
FastCollection collection) {
return collection.service();
}
////////////////////////////////////////////////////////////////////////////
// Textual format.
//
/**
* Default text format for fast collections (parsing not supported).
* It is the format used when printing standard {@code java.util.Collection}
* instances except that the elements themselves are written using
* their {@link TextContext TextContext} format.
*/
@Parallelizable
public static class Format extends TextFormat> {
@Override
public FastCollection