javolution.util.FastMap 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 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.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;
/**
* A high-performance hash map with {@link Realtime real-time} behavior.
* Related to {@link FastCollection}, fast map supports various views.
*
* - {@link #atomic} - Thread-safe view for which all reads are mutex-free
* and map updates (e.g. {@link #putAll putAll}) are atomic.
* - {@link #shared} - View allowing concurrent modifications.
* - {@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 modifications.
* - {@link #entrySet} - {@link FastSet} view over the map entries allowing
* entries to be added/removed.
* - {@link #keySet} - {@link FastSet} view over the map keys allowing keys
* to be added (map entry with {@code null} value).
* - {@link #values} - {@link FastCollection} view over the map values (add not supported).
*
* 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.
*
* Fast maps can advantageously replace any of the standard java.util
maps.
* [code]
* FastMap hashMap = new FastMap();
* FastMap concurrentHashMap = new FastMap().shared(); // FastMap implements ConcurrentMap interface.
* FastMap linkedHashMap = new FastMap(); // Deterministic iteration order (insertion order).
* FastMap treeMap = new FastSortedMap();
* FastMap concurrentSkipListMap = new FastSortedMap().shared();
* FastMap identityHashMap = new FastMap(Equalities.IDENTITY);[/code]
* and adds more ...
* [code]
* FastMap atomicMap = new FastMap().atomic(); // Mutex-free access, all updates (e.g. putAll) atomics (unlike ConcurrentHashMap).
* FastMap atomicTree = new FastSortedMap().atomic(); // Mutex-free access, all updates (e.g. putAll) atomics.
* FastMap parallelMap = new FastMap().parallel(); // Map actions (perform/update) performed concurrently.
* FastMap linkedConcurrentHashMap = new FastMap().shared(); // No equivalent in java.util !
* FastMap lexicalHashMap = new FastMap(Equalities.LEXICAL); // Allows for value retrieval using any CharSequence key.
* FastMap fastStringHashMap = new FastMap(Equalities.LEXICAL_FAST); // Same with faster hashcode calculations.
* ...[/code]
*
* Of course all views (entry, key, values) over a fast map are fast collections
* and allow parallel processing.
* [code]
* Consumer> removeNull = new Consumer>() {
* public void accept(Collection view) {
* Iterator it = view.iterator();
* while (it.hasNext()) {
* if (it.next() == null) it.remove();
* }
* }
* };
* FastMap 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]
*
* @author Jean-Marie Dautelle
* @version 6.0, July 21, 2013
*/
@Realtime
@DefaultTextFormat(FastMap.Text.class)
public class FastMap implements Map, ConcurrentMap,
Serializable {
private static final long serialVersionUID = 0x600L; // Version.
/**
* Holds the actual map service implementation.
*/
private final MapService service;
/**
* Returns a new map holding the same entries as the specified
* map (convenience method).
*/
public static FastMap of(Map extends K, ? extends V> that) {
FastMap map = new FastMap();
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(keyEquality, valueEquality);
}
/**
* Creates a map backed up by the specified service implementation.
*/
protected FastMap(MapService 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 thread-safe
* (no {@link ConcurrentModificationException} possible).
*/
@Parallelizable(mutexFree = true, comment = "Except for write operations, all read operations are mutex-free.")
public FastMap atomic() {
return new FastMap(new AtomicMapImpl(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
* 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 FastMap shared() {
return new FastMap(new SharedMapImpl(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 parallel() {
return new FastMap(new ParallelMapImpl(service));
}
/**
* Returns a sequential view of this collection. Using this view,
* all closure-based iterations are performed sequentially.
*/
public FastMap sequential() {
return new FastMap(new SequentialMapImpl(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 unmodifiable() {
return new FastMap(new UnmodifiableMapImpl(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 keySet() {
return new FastSet(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 values() {
return new FastCollection() {
private static final long serialVersionUID = 0x600L; // Version.
private final CollectionService serviceValues = service.values();
@Override
protected CollectionService 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> entrySet() {
return new FastSet>(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> action) {
service().perform((Consumer>) 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> action) {
service().update((Consumer>) 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 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 true
if this map is considered equals to the
* one specified; false
otherwise.
*/
@Override
@Realtime(limit = LINEAR)
public boolean equals(Object obj) {
return service.equals(obj);
}
@Override
public int hashCode() {
return service.hashCode();
}
/**
* 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 service() {
return service;
}
/**
* Default text format for fast maps (parsing not supported).
*/
@Parallelizable
public static class Text extends TextFormat> {
@Override
public FastMap