/*
* 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 java.io.IOException;
import java.io.Serializable;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
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.internal.map.AtomicMapImpl;
import javolution.util.internal.map.FastMapImpl;
import javolution.util.internal.map.ParallelMapImpl;
import javolution.util.internal.map.SequentialMapImpl;
import javolution.util.internal.map.SharedMapImpl;
import javolution.util.internal.map.UnmodifiableMapImpl;
import javolution.util.service.CollectionService;
import javolution.util.service.MapService;
/**
* <p> A high-performance hash map with {@link Realtime real-time} behavior.
* Related to {@link FastCollection}, fast map supports various views.
* <ul>
* <li>{@link #atomic} - Thread-safe view for which all reads are mutex-free
* and map updates (e.g. {@link #putAll putAll}) are atomic.</li>
* <li>{@link #shared} - View allowing concurrent modifications.</li>
* <li>{@link #parallel} - A view allowing parallel processing including {@link #update updates}.</li>
* <li>{@link #sequential} - View disallowing parallel processing.</li>
* <li>{@link #unmodifiable} - View which does not allow any modifications.</li>
* <li>{@link #entrySet} - {@link FastSet} view over the map entries allowing
* entries to be added/removed.</li>
* <li>{@link #keySet} - {@link FastSet} view over the map keys allowing keys
* to be added (map entry with {@code null} value).</li>
* <li>{@link #values} - {@link FastCollection} view over the map values (add not supported).</li>
* </ul>
* <p> The iteration order over the map keys, values or entries is deterministic
* (unlike {@link java.util.HashMap}). It is either the insertion order (default)
* or the key order for the {@link FastSortedMap} subclass.
* This class permits {@code null} keys.</p>
*
* <p> Fast maps can advantageously replace any of the standard <code>java.util</code> maps.</p>
* [code]
* FastMap<Foo, Bar> hashMap = new FastMap<Foo, Bar>();
* FastMap<Foo, Bar> concurrentHashMap = new FastMap<Foo, Bar>().shared(); // FastMap implements ConcurrentMap interface.
* FastMap<Foo, Bar> linkedHashMap = new FastMap<Foo, Bar>(); // Deterministic iteration order (insertion order).
* FastMap<Foo, Bar> treeMap = new FastSortedMap<Foo, Bar>();
* FastMap<Foo, Bar> concurrentSkipListMap = new FastSortedMap<Foo, Bar>().shared();
* FastMap<Foo, Bar> identityHashMap = new FastMap<Foo, Bar>(Equalities.IDENTITY);[/code]</p>
* <p> and adds more ...
* [code]
* FastMap<Foo, Bar> atomicMap = new FastMap<Foo, Bar>().atomic(); // Mutex-free access, all updates (e.g. putAll) atomics (unlike ConcurrentHashMap).
* FastMap<Foo, Bar> atomicTree = new FastSortedMap<Foo, Bar>().atomic(); // Mutex-free access, all updates (e.g. putAll) atomics.
* FastMap<Foo, Bar> parallelMap = new FastMap<Foo, Bar>().parallel(); // Map actions (perform/update) performed concurrently.
* FastMap<Foo, Bar> linkedConcurrentHashMap = new FastMap<Foo, Bar>().shared(); // No equivalent in java.util !
* FastMap<String, Bar> lexicalHashMap = new FastMap<String, Bar>(Equalities.LEXICAL); // Allows for value retrieval using any CharSequence key.
* FastMap<String, Bar> fastStringHashMap = new FastMap<String, Bar>(Equalities.LEXICAL_FAST); // Same with faster hashcode calculations.
* ...[/code]</p>
*
* <p> Of course all views (entry, key, values) over a fast map are fast collections
* and allow parallel processing.
* [code]
* Consumer<Collection<String>> removeNull = new Consumer<Collection<String>>() {
* public void accept(Collection<String> view) {
* Iterator<String> it = view.iterator();
* while (it.hasNext()) {
* if (it.next() == null) it.remove();
* }
* }
* };
* FastMap<Person, String> names = ...
* names.values().update(removeNull); // Remove all entries with null values.
* names.atomic().values().update(removeNull); // Same but performed atomically.
* names.parallel().values().update(removeNull); // Same but performed in parallel.
* [/code]</p>
*
* @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle </a>
* @version 6.0, July 21, 2013
*/
@Realtime
@DefaultTextFormat(FastMap.Text.class)
public class FastMap<K, V> implements Map<K, V>, ConcurrentMap<K, V>,
Serializable {
private static final long serialVersionUID = 0x600L; // Version.
/**
* Holds the actual map service implementation.
*/
private final MapService<K, V> service;
/**
* Returns a new map holding the same entries as the specified
* map (convenience method).
*/
public static <K,V> FastMap<K,V> of(Map<? extends K, ? extends V> that) {
FastMap<K,V> map = new FastMap<K,V>();
map.putAll(that);
return map;
}
/**
* Creates an empty fast map.
*/
public FastMap() {
this(Equalities.STANDARD);
}
/**
* Creates an empty fast map using the specified comparator for keys
* equality.
*/
public FastMap(Equality<? super K> keyEquality) {
this(keyEquality, Equalities.STANDARD);
}
/**
* Creates an empty fast map using the specified comparators for keys
* equality and values equality.
*/
public FastMap(Equality<? super K> keyEquality,
Equality<? super V> valueEquality) {
service = new FastMapImpl<K, V>(keyEquality, valueEquality);
}
/**
* Creates a map backed up by the specified service implementation.
*/
protected FastMap(MapService<K, V> service) {
this.service = service;
}
////////////////////////////////////////////////////////////////////////////
// Views.
//
/**
* Returns an atomic view over this map. All operations that write
* or access multiple elements in the map (such as putAll(),
* keySet().retainAll(), ...) are atomic.
* Iterators on atomic collections are <b>thread-safe</b>
* (no {@link ConcurrentModificationException} possible).
*/
@Parallelizable(mutexFree = true, comment = "Except for write operations, all read operations are mutex-free.")
public FastMap<K, V> atomic() {
return new FastMap<K, V>(new AtomicMapImpl<K, V>(service));
}
/**
* Returns a thread-safe view over this map. The shared view
* allows for concurrent read as long as there is no writer.
* The default implementation is based on <a href=
* "http://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock">
* readers-writers locks</a> giving priority to writers.
* Iterators on shared collections are <b>thread-safe</b>
* (no {@link ConcurrentModificationException} possible).
*/
@Parallelizable(mutexFree = false, comment = "Use multiple-readers/single-writer lock.")
public FastMap<K, V> shared() {
return new FastMap<K, V>(new SharedMapImpl<K, V>(service));
}
/**
* Returns a parallel map. Parallel maps affect closure-based operations
* over the map or any of its views (entry, key, values, etc.), all others
* operations behaving the same. Parallel maps do not require this map
* to be thread-safe (internal synchronization).
*
* @see #perform(Consumer)
* @see #update(Consumer)
* @see FastCollection#parallel()
*/
public FastMap<K, V> parallel() {
return new FastMap<K, V>(new ParallelMapImpl<K, V>(service));
}
/**
* Returns a sequential view of this collection. Using this view,
* all closure-based iterations are performed sequentially.
*/
public FastMap<K, V> sequential() {
return new FastMap<K, V>(new SequentialMapImpl<K, V>(service));
}
/**
* Returns an unmodifiable view over this map. Any attempt to
* modify the map through this view will result into
* a {@link java.lang.UnsupportedOperationException} being raised.
*/
public FastMap<K, V> unmodifiable() {
return new FastMap<K, V>(new UnmodifiableMapImpl<K, V>(service));
}
/**
* Returns a set view of the keys contained in this map.
* The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. The set supports
* adding new keys for which the corresponding entry value
* is always {@code null}.
*/
public FastSet<K> keySet() {
return new FastSet<K>(service.keySet());
}
/**
* Returns a collection view of the values contained in this map.
* The collection is backed by the map, so changes to the map are
* reflected in the collection, and vice-versa. The collection
* supports removing values (hence entries) but not adding new values.
*/
public FastCollection<V> values() {
return new FastCollection<V>() {
private static final long serialVersionUID = 0x600L; // Version.
private final CollectionService<V> serviceValues = service.values();
@Override
protected CollectionService<V> service() {
return serviceValues;
}
};
}
/**
* Returns a set view of the mappings contained in
* this map. The set is backed by the map, so changes to the map are
* reflected in the set, and vice-versa. The set
* support adding/removing entries. As far as the set is concerned,
* two entries are considered equals if they have the same keys regardless
* of their values.
*/
public FastSet<Entry<K, V>> entrySet() {
return new FastSet<Entry<K, V>>(service.entrySet());
}
////////////////////////////////////////////////////////////////////////////
// Closures operations.
//
/**
* Executes the specified read-only action on this map.
* That logic may be performed concurrently on sub-maps
* if this map is {@link #parallel() parallel}.
*
* @param action the read-only action.
* @throws UnsupportedOperationException if the action tries to update
* this map.
* @throws ClassCastException if the action type is not compatible with
* this map (e.g. action on sorted map and this is a hash map).
* @see #update(Consumer)
*/
@Realtime(limit = LINEAR)
@SuppressWarnings("unchecked")
public void perform(Consumer<? extends Map<K, V>> action) {
service().perform((Consumer<MapService<K, V>>) action, service());
}
/**
* Executes the specified update action on this map.
* That logic may be performed concurrently on sub-maps
* if this map is {@link #parallel() parallel}.
* For {@link #atomic() atomic} maps 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 map (e.g. action on sorted map and this is a hash map).
* @see #perform(Consumer)
*/
@Realtime(limit = LINEAR)
@SuppressWarnings("unchecked")
public void update(Consumer<? extends Map<K, V>> action) {
service().update((Consumer<MapService<K, V>>) action, service());
}
////////////////////////////////////////////////////////////////////////////
// Map Interface.
//
/** Returns the number of entries/keys/values in this map. */
@Override
@Realtime(limit = CONSTANT)
public int size() {
return service.size();
}
/** Indicates if this map is empty */
@Override
@Realtime(limit = CONSTANT)
public boolean isEmpty() {
return service.isEmpty();
}
/** Indicates if this map contains the specified key. */
@Override
@Realtime(limit = CONSTANT)
public boolean containsKey(Object key) {
return service.containsKey(key);
}
/** Indicates if this map contains the specified value. */
@Override
@Realtime(limit = LINEAR)
public boolean containsValue(Object value) {
return service.containsValue(value);
}
/** Returns the value for the specified key. */
@Override
@Realtime(limit = CONSTANT)
public V get(Object key) {
return service.get(key);
}
/** Associates the specified value with the specified key. */
@Override
@Realtime(limit = CONSTANT)
public V put(K key, V value) {
return service.put(key, value);
}
/** Adds the specified map entries to this map. */
@Override
@Realtime(limit = LINEAR)
public void putAll(Map<? extends K, ? extends V> that) {
service.putAll(that);
}
/** Removes the entry for the specified key. */
@Override
@Realtime(limit = CONSTANT)
public V remove(Object key) {
return service.remove(key);
}
/** Removes all this map's entries. */
@Override
@Realtime(limit = CONSTANT)
public void clear() {
service.clear();
}
////////////////////////////////////////////////////////////////////////////
// ConcurrentMap Interface.
//
/** Associates the specified value with the specified key only if the
* specified key has no current mapping. */
@Override
@Realtime(limit = CONSTANT)
public V putIfAbsent(K key, V value) {
return service.putIfAbsent(key, value);
}
/** Removes the entry for a key only if currently mapped to a given value. */
@Override
@Realtime(limit = CONSTANT)
public boolean remove(Object key, Object value) {
return service.remove(key, value);
}
/** Replaces the entry for a key only if currently mapped to a given value. */
@Override
@Realtime(limit = CONSTANT)
public boolean replace(K key, V oldValue, V newValue) {
return service.replace(key, oldValue, newValue);
}
/** Replaces the entry for a key only if currently mapped to some value. */
@Override
@Realtime(limit = CONSTANT)
public V replace(K key, V value) {
return service.replace(key, value);
}
////////////////////////////////////////////////////////////////////////////
// Misc.
//
/**
* Returns this map with the specified [key, value] entry added.
* This method allow for chaining.
*/
public FastMap<K, V> add(K key, V value) {
put(key, value);
return this;
}
/**
* Compares the specified object with this map for equality.
* This method follows the {@link Map#equals(Object)} specification
* regardless of the map's comparators.
*
* @param obj the object to be compared for equality with this map
* @return <code>true</code> if this map is considered equals to the
* one specified; <code>false</code> otherwise.
*/
@Override
@Realtime(limit = LINEAR)
public boolean equals(Object obj) {
return service.equals(obj);
}
@Override
public int hashCode() {
return service.hashCode();
}
/**
* Returns an immutable reference over this map. The immutable
* value is an {@link #unmodifiable() unmodifiable} view of this map
* for which the caller guarantees that no change will ever be made
* (e.g. there is no reference left to the original map).
*/
public <T extends Map<K, V>> Immutable<T> immutable() {
return new Immutable<T>() {
@SuppressWarnings("unchecked")
final T value = (T) unmodifiable();
@Override
public T value() {
return value;
}
};
}
/**
* Returns the string representation of this map using its
* {@link TextContext contextual format}.
*/
@Override
@Realtime(limit = LINEAR)
public String toString() {
return TextContext.getFormat(FastMap.class).format(this);
}
/**
* Returns this map service implementation.
*/
protected MapService<K, V> service() {
return service;
}
/**
* Default text format for fast maps (parsing not supported).
*/
@Parallelizable
public static class Text extends TextFormat<FastMap<?, ?>> {
@Override
public FastMap<Object, Object> parse(CharSequence csq, Cursor cursor)
throws IllegalArgumentException {
throw new UnsupportedOperationException();
}
@Override
public Appendable format(FastMap<?, ?> that, final Appendable dest)
throws IOException {
Iterator<?> i = that.entrySet().iterator();
dest.append('{');
while (i.hasNext()) {
Map.Entry<?,?> entry = (Map.Entry<?, ?>) i.next();
TextContext.format(entry.getKey(), dest);
dest.append('=');
TextContext.format(entry.getValue(), dest);
if (i.hasNext()) {
dest.append(',').append(' ');
}
}
return dest.append('}');
}
}
}