All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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.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.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.ComparatorCollectionImpl;
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. *

* *

Unmodifiable collections are not always {@link javolution.lang.Constant constant}. * Constant/immutable collections can be obtained from collection specializations. * [code] * ConstantTable> winners = ConstantTable.of("John Deuff", "Otto Graf", "Sim Kamil"); * // Immutability is guaranteed by construction (no reference left on the array 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 = new FastTable().atomic(); * ... * // 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 = FastTable.of("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] * FastTable persons = new FastTable().parallel(); * ... * // Since persons is parallel, the search is done concurrently. * Person john = persons.filtered(new Predicate() { * public boolean test(Person person) { * return person.getName().equals("John"); * } * }).any(Person.class);[/code]

* *

With Java 8, closures are greatly simplified using lambda expressions. * [code] * Person john = persons.filtered(person -> person.getName().equals("John")).any(Person.class); // Same as above. * tasks.parallel().forEach(task -> task.run()); * 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.Text.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. Closure-based operations are * performed {@link javolution.context.ConcurrentContext in parallel} * on {@link CollectionService#split sub-views} of this collection. * The number of parallel views is equals to * {@link javolution.context.ConcurrentContext#getConcurrency() * concurrency}{@code + 1}. * * @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 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 function) { return new MappedCollectionImpl(service(), function); } /** * Returns a view using the specified comparator for element equality * and sorting. * * @see #contains(Object) * @see #remove(Object) * @see #sorted() * @see #distinct() */ public FastCollection comparator(Comparator cmp) { return new ComparatorCollectionImpl(service(), comparator()); } /** * Returns a view exposing elements sorted according to this * collection {@link #comparator()}. * * @see #comparator() * @see #comparator(Comparator) */ public FastCollection sorted() { return new SortedCollectionImpl(service()); } /** * Returns a view exposing elements sorted according to the specified * comparator (convenience method). * * @return {@code comparator(cmp).sorted()} */ public FastCollection sorted(Comparator cmp) { return comparator(cmp).sorted(); } /** * 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 action on this collection. * That logic may be performed concurrently (on parallel views) * if this collection is {@link #parallel() parallel}. * * * @param action the read-only action. * @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> action) { service().perform((Consumer>) action, service()); } /** * Executes the specified update action on this collection. * For {@link #atomic() atomic} collections the update is atomic * (either concurrent readers see the full result of the action or * nothing). * The update may be performed concurrently (through parallel views) * if this collection is {@link #parallel() parallel}. * * @param action the update action. * @throws ClassCastException if the action type is not compatible with * this collection (e.g. action on a {@link java.util.Set Set} * and this is a {@link java.util.List List}). * @see #perform(Consumer) */ @SuppressWarnings("unchecked") @Realtime(limit = LINEAR) public void update(Consumer> 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 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 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. * @see #any(Class) * @see #min() * @see #max() */ @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 = "Could iterate 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 = "Could iterate the whole collection (e.g. filtered view).") public boolean isEmpty() { return iterator().hasNext(); } /** Returns the size of this collection. */ @Override @Realtime(limit = LINEAR, comment = "Could count the elements (e.g. filtered view).") public int size() { return service().size(); } /** Removes all elements from this collection. */ @Override @Realtime(limit = LINEAR, comment = "Could remove the elements one at a time.") public void clear() { service().clear(); } /** Indicates if this collection contains the specified element. */ @Override @Realtime(limit = LINEAR, comment = "Could search the whole collection.") public boolean contains(Object searched) { return service().contains(searched); } /** Removes the specified element from this collection.*/ @Override @Realtime(limit = LINEAR, comment = "Could search 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 = "Sorted view iterator require sorting the elements.") public Iterator iterator() { return service().iterator(); } /** Adds all the specified elements to this collection. */ @Override @Realtime(limit = LINEAR) public boolean addAll(final Collection 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). * The search is performed concurrently if this collection is * {@link #parallel() parallel}. * * @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. * The search is performed concurrently if this collection is * {@link #parallel() parallel}. * * @return {@code reduce(Reducers.min(comparator()))} * @see Reducers#min */ @Realtime(limit = LINEAR) public E min() { return reduce((Reducer)Reducers.min(comparator())); // Cast only necessary on JDK 1.6 ! } /** * Returns the largest element of this collection using this * collection {@link #comparator() comparator} (convenience method). * Returns {@code null} if this collection is empty. * The search is performed concurrently if this collection is * {@link #parallel() parallel}. * * @return {@code reduce(Reducers.max(comparator()))} * @see Reducers#max */ @Realtime(limit = LINEAR) public E max() { return reduce((Reducer)Reducers.max(comparator())); // Cast only necessary on JDK 1.6 ! } /** * Returns the comparator used by this collection for element equality * and/or ordering if this collection is sorted. The comparator is * used by methods such as {@link #contains(Object)} or * {@link #remove(Object)} but it is ignored when comparing collections * for {@link #equals(Object) equality}. */ @Realtime(limit = CONSTANT) public Equality comparator() { return service().comparator(); } /** * Compares the specified object with this collection for equality. * This method follows the {@link Collection#equals(Object)} specification * regardless of the collection's {@link #comparator comparator}. * For example, two sorted sets using distinct comparators are considered * equals as long as they both hold the same elements. * * @param obj the object to be compared for equality with this collection * @return true if this collection is considered equals to the * one specified; false otherwise. */ @Override @Realtime(limit = LINEAR) public boolean equals(Object obj) { return service().equals(obj); } /** * Returns the hash code of this collection. This method always follows * the {@link Collection#hashCode()} specification regardless of the * collection's comparator. * * @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}. * * @see TextContext */ @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(); } /** * Default text format for fast collections (parsing not supported). */ @Parallelizable public static class Text extends TextFormat> { @Override public FastCollection parse(CharSequence csq, Cursor cursor) throws IllegalArgumentException { throw new UnsupportedOperationException(); } @Override public Appendable format(FastCollection that, final Appendable dest) throws IOException { Iterator i = that.iterator(); dest.append('['); while (i.hasNext()) { TextContext.format(i.next(), dest); if (i.hasNext()) { dest.append(',').append(' '); } } return dest.append(']'); } } }