package fj.data;
import fj.*;
import fj.function.Effect1;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import static fj.P.p;
import static fj.data.List.iterableList;
import static fj.data.Option.fromNull;
/**
* A mutable hash map providing O(1) lookup.
*
* @version %build.number%
* @see java.util.HashMap
*/
public final class HashMap<K, V> implements Iterable<K> {
private final class Key {
final K k;
Key(final K k) {
this.k = k;
}
@SuppressWarnings("unchecked")
public boolean equals(final Object o) {
return o instanceof HashMap.Key && e.eq(k, ((Key) o).k);
}
public int hashCode() {
return h.hash(k);
}
}
/**
* Returns an iterator for this map's keys. This method exists to permit the use in a <code>for</code>-each loop.
*
* @return A iterator for this map's keys.
*/
public Iterator<K> iterator() {
return keys().iterator();
}
private final java.util.HashMap<Key, V> m;
private final Equal<K> e;
private final Hash<K> h;
/**
* Construct a hash map with the given equality and hashing strategy.
*
* @param e The equality strategy.
* @param h The hashing strategy.
*/
public HashMap(final Equal<K> e, final Hash<K> h) {
m = new java.util.HashMap<>();
this.e = e;
this.h = h;
}
public HashMap(java.util.Map<K, V> map, final Equal<K> e, final Hash<K> h) {
this(e, h);
for (Map.Entry<K, V> entry : map.entrySet()) {
set(entry.getKey(), entry.getValue());
}
}
/**
* Construct a hash map with the given equality and hashing strategy.
*
* @param e The equality strategy.
* @param h The hashing strategy.
* @param initialCapacity The initial capacity.
*/
public HashMap(final Equal<K> e, final Hash<K> h, final int initialCapacity) {
m = new java.util.HashMap<>(initialCapacity);
this.e = e;
this.h = h;
}
public HashMap(java.util.Map<K, V> map) {
this(map, Equal.anyEqual(), Hash.anyHash());
}
/**
* Construct a hash map with the given equality and hashing strategy.
*
* @param e The equality strategy.
* @param h The hashing strategy.
* @param initialCapacity The initial capacity.
* @param loadFactor The load factor.
*/
public HashMap(final Equal<K> e, final Hash<K> h, final int initialCapacity, final float loadFactor) {
m = new java.util.HashMap<>(initialCapacity, loadFactor);
this.e = e;
this.h = h;
}
/**
* Construct a hash map that uses {@link Object#equals} and {@link Object#hashCode}.
*
* @return A new hash map that uses {@link Object#equals} and {@link Object#hashCode}.
*/
public static <K, V> HashMap<K, V> hashMap() {
return hashMap(Equal.anyEqual(), Hash.anyHash());
}
/**
* Construct a hash map.
*
* @return A new hash map.
*/
public static <K, V> HashMap<K, V> hashMap(final Equal<K> e, final Hash<K> h) {
return new HashMap<>(e, h);
}
/**
* Compare two key values for equality using the underlying equality strategy.
*
* @param k1 One key value to compare.
* @param k2 The other key value to compare.
* @return <code>true</code> if the two key values are equal, <code>false</code> otherwise.
*/
public boolean eq(final K k1, final K k2) {
return e.eq(k1, k2);
}
/**
* Compute the hash of the given key value using the underlying hashing strategy.
*
* @param k The key value to computer the hash of.
* @return The hash of the given key value.
*/
public int hash(final K k) {
return h.hash(k);
}
/**
* Returns a potential value that the given key maps to.
*
* @param k The key to look up in the hash map.
* @return A potential value for the given key.
*/
public Option<V> get(final K k) {
return fromNull(m.get(new Key(k)));
}
/**
* A curried version of {@link #get(Object)}.
*
* @return A curried version of {@link #get(Object)}.
*/
public F<K, Option<V>> get() {
return this::get;
}
/**
* Clear all entries from this hash map.
*/
public void clear() {
m.clear();
}
/**
* Determines if the given key value exists in this hash map.
*
* @param k The key value to look for in this hash map.
* @return <code>true</code> if this hash map contains the given key, <code>false</code> otherwise.
*/
public boolean contains(final K k) {
return m.containsKey(new Key(k));
}
/**
* Returns all key entries in this hash map.
*
* @return All key entries in this hash map.
*/
public List<K> keys() {
final List.Buffer<K> b = new List.Buffer<>();
for (final Key k : m.keySet()) {
b.snoc(k.k);
}
return b.toList();
}
/**
* Returns all values in this hash map.
*
* @return All values in this hash map.
*/
public List<V> values() {
return iterableList(m.values());
}
/**
* Determines if this hash map has any entries.
*
* @return <code>true</code> if this hash map has no entries, <code>false</code> otherwise.
*/
public boolean isEmpty() {
return m.isEmpty();
}
/**
* Returns the number of entries in this hash map.
*
* @return The number of entries in this hash map.
*/
public int size() {
return m.size();
}
/**
* Inserts the given key and value association into the hash map.
*
* @param k The key to insert.
* @param v The value to insert.
*/
public void set(final K k, final V v) {
if (v != null) {
m.put(new Key(k), v);
}
}
/**
* Deletes the entry in the hash map that corresponds to the given key.
*
* @param k The key to delete from this hash map.
*/
public void delete(final K k) {
m.remove(new Key(k));
}
/**
* Deletes the entry in the hash map that corresponds to the given key and returns any associated value.
*
* @param k The key to delete from this hash map.
* @return The value that was associated with the given key, if there was one.
*/
public Option<V> getDelete(final K k) {
return fromNull(m.remove(new Key(k)));
}
public <A, B> HashMap<A, B> map(F<K, A> keyFunction,
F<V, B> valueFunction,
Equal<A> equal, Hash<A> hash) {
final HashMap<A, B> hashMap = new HashMap<>(equal, hash);
for (K key : keys()) {
final A newKey = keyFunction.f(key);
final B newValue = valueFunction.f(get(key).some());
hashMap.set(newKey, newValue);
}
return hashMap;
}
public <A, B> HashMap<A, B> map(F<K, A> keyFunction,
F<V, B> valueFunction) {
return map(keyFunction, valueFunction, Equal.anyEqual(), Hash.anyHash());
}
public <A, B> HashMap<A, B> map(F<P2<K, V>, P2<A, B>> function, Equal<A> equal, Hash<A> hash) {
return iterableHashMap(equal, hash, toStream().map(function));
}
public <A, B> HashMap<A, B> map(F<P2<K, V>, P2<A, B>> function) {
return iterableHashMap(toStream().map(function));
}
public <A> HashMap<A, V> mapKeys(F<K, A> keyFunction, Equal<A> equal, Hash<A> hash) {
return map(keyFunction, Function.identity(), equal, hash);
}
public <A> HashMap<A, V> mapKeys(F<K, A> function) {
return mapKeys(function, Equal.anyEqual(), Hash.anyHash());
}
public <B> HashMap<K, B> mapValues(F<V, B> function) {
return map(Function.identity(), function, e, h);
}
public void foreachDoEffect(Effect1<P2<K, V>> effect) {
toStream().foreachDoEffect(effect);
}
public void foreach(F<P2<K, V>, Unit> function) {
toStream().foreach(function);
}
public List<P2<K, V>> toList() {
return keys().map(k -> p(k, get(k).some()));
}
/**
* Projects an immutable collection of this hash map.
*
* @return An immutable collection of this hash map.
*/
public Collection<P2<K, V>> toCollection() {
return toList().toCollection();
}
public Stream<P2<K, V>> toStream() {
return toList().toStream();
}
public Option<P2<K, V>> toOption() {
return toList().headOption();
}
public Array<P2<K, V>> toArray() {
return toList().toArray();
}
public java.util.Map<K, V> toMap() {
final java.util.HashMap<K,V> result = new java.util.HashMap<>();
for (K key : keys()) {
result.put(key, get(key).some());
}
return result;
}
/**
* Converts the Iterable to a HashMap
*
* @deprecated As of release 4.5, use {@link #iterableHashMap(Iterable)}
*/
@Deprecated
public static <K, V> HashMap<K, V> from(final Iterable<P2<K, V>> entries) {
return iterableHashMap(entries);
}
public static <K, V> HashMap<K, V> fromMap(java.util.Map<K, V> map) {
return fromMap(Equal.anyEqual(), Hash.anyHash(), map);
}
public static <K, V> HashMap<K, V> fromMap(Equal<K> eq, Hash<K> h, java.util.Map<K, V> map) {
HashMap<K, V> m = hashMap(eq, h);
for (Map.Entry<K, V> e: map.entrySet()) {
m.set(e.getKey(), e.getValue());
}
return m;
}
/**
* Converts the Iterable to a HashMap
*
* @deprecated As of release 4.5, use {@link #iterableHashMap}
*/
@Deprecated
public static <K, V> HashMap<K, V> from(final Iterable<P2<K, V>> entries, final Equal<K> equal, final Hash<K> hash) {
return iterableHashMap(equal, hash, entries);
}
/**
* Converts the Iterable to a HashMap
*/
public static <K, V> HashMap<K, V> iterableHashMap(final Equal<K> equal, final Hash<K> hash, final Iterable<P2<K, V>> entries) {
final HashMap<K, V> map = new HashMap<>(equal, hash);
for (P2<K, V> entry : entries) {
map.set(entry._1(), entry._2());
}
return map;
}
/**
* Converts the Iterable to a HashMap
*/
public static <K, V> HashMap<K, V> iterableHashMap(final Iterable<P2<K, V>> entries) {
return iterableHashMap(Equal.anyEqual(), Hash.anyHash(), entries);
}
/**
* Converts the array to a HashMap
*/
@SafeVarargs
public static <K, V> HashMap<K, V> arrayHashMap(final P2<K, V>...entries) {
return iterableHashMap(Array.array(entries));
}
/**
* Converts the array to a HashMap
*/
@SafeVarargs
public static <K, V> HashMap<K, V> arrayHashMap(final Equal<K> equal, final Hash<K> hash, final P2<K, V>...entries) {
return iterableHashMap(equal, hash, Array.array(entries));
}
/**
* Converts the Iterator to a HashMap
*/
public static <K, V> HashMap<K, V> iteratorHashMap(final Equal<K> equal, final Hash<K> hash, final Iterator<P2<K, V>> entries) {
return iterableHashMap(equal, hash, () -> entries);
}
/**
* Converts the Iterator to a HashMap
*/
public static <K, V> HashMap<K, V> iteratorHashMap(final Iterator<P2<K, V>> entries) {
return iterableHashMap(() -> entries);
}
}