javaslang.collection.HashMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javaslang Show documentation
Show all versions of javaslang Show documentation
Javaslang is a Java standard library extension built for Java 8 and above.
/* / \____ _ _ ____ ______ / \ ____ __ _______
* / / \/ \ / \/ \ / /\__\/ // \/ \ // /\__\ JΛVΛSLΛNG
* _/ / /\ \ \/ / /\ \\__\\ \ // /\ \ /\\/ \ /__\ \ Copyright 2014-2016 Javaslang, http://javaslang.io
* /___/\_/ \_/\____/\_/ \_/\__\/__/\__\_/ \_// \__/\_____/ Licensed under the Apache License, Version 2.0
*/
package javaslang.collection;
import javaslang.Kind2;
import javaslang.Tuple;
import javaslang.Tuple2;
import javaslang.control.Option;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.*;
import java.util.stream.Collector;
/**
* An immutable {@code HashMap} implementation based on a
* Hash array mapped trie (HAMT).
*
* @author Ruslan Sennov, Patryk Najda, Daniel Dietrich
* @since 2.0.0
*/
public final class HashMap implements Kind2, K, V>, Map, Serializable {
private static final long serialVersionUID = 1L;
private static final HashMap, ?> EMPTY = new HashMap<>(HashArrayMappedTrie.empty());
private final HashArrayMappedTrie trie;
private HashMap(HashArrayMappedTrie trie) {
this.trie = trie;
}
/**
* Returns a {@link java.util.stream.Collector} which may be used in conjunction with
* {@link java.util.stream.Stream#collect(java.util.stream.Collector)} to obtain a {@link javaslang.collection.HashMap}.
*
* @param The key type
* @param The value type
* @return A {@link javaslang.collection.HashMap} Collector.
*/
public static Collector, ArrayList>, HashMap> collector() {
final Supplier>> supplier = ArrayList::new;
final BiConsumer>, Tuple2> accumulator = ArrayList::add;
final BinaryOperator>> combiner = (left, right) -> {
left.addAll(right);
return left;
};
final Function>, HashMap> finisher = HashMap::ofEntries;
return Collector.of(supplier, accumulator, combiner, finisher);
}
@SuppressWarnings("unchecked")
public static HashMap empty() {
return (HashMap) EMPTY;
}
/**
* Narrows a widened {@code HashMap extends K, ? extends V>} to {@code HashMap}
* by performing a type safe-cast. This is eligible because immutable/read-only
* collections are covariant.
*
* @param hashMap A {@code HashMap}.
* @param Key type
* @param Value type
* @return the given {@code hashMap} instance as narrowed type {@code HashMap}.
*/
@SuppressWarnings("unchecked")
public static HashMap narrow(HashMap extends K, ? extends V> hashMap) {
return (HashMap) hashMap;
}
/**
* Returns a singleton {@code HashMap}, i.e. a {@code HashMap} of one element.
*
* @param entry A map entry.
* @param The key type
* @param The value type
* @return A new Map containing the given entry
*/
public static HashMap of(Tuple2 extends K, ? extends V> entry) {
return new HashMap<>(HashArrayMappedTrie. empty().put(entry._1, entry._2));
}
/**
* Returns a {@code HashMap}, from a source java.util.Map.
*
* @param map A map entry.
* @param The key type
* @param The value type
* @return A new Map containing the given map
*/
public static HashMap ofAll(java.util.Map extends K, ? extends V> map) {
Objects.requireNonNull(map, "map is null");
HashArrayMappedTrie tree = HashArrayMappedTrie.empty();
for (java.util.Map.Entry extends K, ? extends V> entry : map.entrySet()) {
tree = tree.put(entry.getKey(), entry.getValue());
}
return wrap(tree);
}
/**
* Returns a singleton {@code HashMap}, i.e. a {@code HashMap} of one element.
*
* @param key A singleton map key.
* @param value A singleton map value.
* @param The key type
* @param The value type
* @return A new Map containing the given entry
*/
public static HashMap of(K key, V value) {
return new HashMap<>(HashArrayMappedTrie. empty().put(key, value));
}
/**
* Creates a HashMap of the given list of key-value pairs.
*
* @param pairs A list of key-value pairs
* @param The key type
* @param The value type
* @return A new Map containing the given entries
*/
@SuppressWarnings("unchecked")
public static HashMap of(Object... pairs) {
Objects.requireNonNull(pairs, "pairs is null");
if ((pairs.length & 1) != 0) {
throw new IllegalArgumentException("Odd length of key-value pairs list");
}
HashArrayMappedTrie trie = HashArrayMappedTrie.empty();
for (int i = 0; i < pairs.length; i += 2) {
trie = trie.put((K) pairs[i], (V) pairs[i + 1]);
}
return wrap(trie);
}
/**
* Returns an HashMap containing {@code n} values of a given Function {@code f}
* over a range of integer values from 0 to {@code n - 1}.
*
* @param The key type
* @param The value type
* @param n The number of elements in the HashMap
* @param f The Function computing element values
* @return An HashMap consisting of elements {@code f(0),f(1), ..., f(n - 1)}
* @throws NullPointerException if {@code f} is null
*/
@SuppressWarnings("unchecked")
public static HashMap tabulate(int n, Function super Integer, ? extends Tuple2 extends K, ? extends V>> f) {
Objects.requireNonNull(f, "f is null");
return ofEntries(Collections.tabulate(n, (Function super Integer, ? extends Tuple2>) f));
}
/**
* Returns an HashMap containing {@code n} values supplied by a given Supplier {@code s}.
*
* @param The key type
* @param The value type
* @param n The number of elements in the HashMap
* @param s The Supplier computing element values
* @return An HashMap of size {@code n}, where each element contains the result supplied by {@code s}.
* @throws NullPointerException if {@code s} is null
*/
@SuppressWarnings("unchecked")
public static HashMap fill(int n, Supplier extends Tuple2 extends K, ? extends V>> s) {
Objects.requireNonNull(s, "s is null");
return ofEntries(Collections.fill(n, (Supplier extends Tuple2>) s));
}
/**
* Creates a HashMap of the given entries.
*
* @param entries Map entries
* @param The key type
* @param The value type
* @return A new Map containing the given entries
*/
@SafeVarargs
public static HashMap ofEntries(java.util.Map.Entry extends K, ? extends V>... entries) {
Objects.requireNonNull(entries, "entries is null");
HashArrayMappedTrie trie = HashArrayMappedTrie.empty();
for (java.util.Map.Entry extends K, ? extends V> entry : entries) {
trie = trie.put(entry.getKey(), entry.getValue());
}
return wrap(trie);
}
/**
* Creates a HashMap of the given entries.
*
* @param entries Map entries
* @param The key type
* @param The value type
* @return A new Map containing the given entries
*/
@SafeVarargs
public static HashMap ofEntries(Tuple2 extends K, ? extends V>... entries) {
Objects.requireNonNull(entries, "entries is null");
HashArrayMappedTrie trie = HashArrayMappedTrie.empty();
for (Tuple2 extends K, ? extends V> entry : entries) {
trie = trie.put(entry._1, entry._2);
}
return wrap(trie);
}
/**
* Creates a HashMap of the given entries.
*
* @param entries Map entries
* @param The key type
* @param The value type
* @return A new Map containing the given entries
*/
@SuppressWarnings("unchecked")
public static HashMap ofEntries(Iterable extends Tuple2 extends K, ? extends V>> entries) {
Objects.requireNonNull(entries, "entries is null");
if (entries instanceof HashMap) {
return (HashMap) entries;
} else {
HashArrayMappedTrie trie = HashArrayMappedTrie.empty();
for (Tuple2 extends K, ? extends V> entry : entries) {
trie = trie.put(entry._1, entry._2);
}
return wrap(trie);
}
}
@Override
public HashMap bimap(Function super K, ? extends K2> keyMapper, Function super V, ? extends V2> valueMapper) {
Objects.requireNonNull(keyMapper, "keyMapper is null");
Objects.requireNonNull(valueMapper, "valueMapper is null");
final Iterator> entries = iterator().map(entry -> Tuple.of(keyMapper.apply(entry._1), valueMapper.apply(entry._2)));
return HashMap.ofEntries(entries);
}
@Override
public boolean containsKey(K key) {
return trie.containsKey(key);
}
@Override
public HashMap distinct() {
return Maps.distinct(this);
}
@Override
public HashMap distinctBy(Comparator super Tuple2> comparator) {
return Maps.distinctBy(this, this::createFromEntries, comparator);
}
@Override
public HashMap distinctBy(Function super Tuple2, ? extends U> keyExtractor) {
return Maps.distinctBy(this, this::createFromEntries, keyExtractor);
}
@Override
public HashMap drop(long n) {
return Maps.drop(this, this::createFromEntries, HashMap::empty, n);
}
@Override
public HashMap dropRight(long n) {
return Maps.dropRight(this, this::createFromEntries, HashMap::empty, n);
}
@Override
public HashMap dropUntil(Predicate super Tuple2> predicate) {
return Maps.dropUntil(this, this::createFromEntries, predicate);
}
@Override
public HashMap dropWhile(Predicate super Tuple2> predicate) {
return Maps.dropWhile(this, this::createFromEntries, predicate);
}
@Override
public HashMap filter(Predicate super Tuple2> predicate) {
return Maps.filter(this, this::createFromEntries, predicate);
}
@Override
public HashMap flatMap(BiFunction super K, ? super V, ? extends Iterable>> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
return foldLeft(HashMap. empty(), (acc, entry) -> {
for (Tuple2 extends K2, ? extends V2> mappedEntry : mapper.apply(entry._1, entry._2)) {
acc = acc.put(mappedEntry);
}
return acc;
});
}
@Override
public Option get(K key) {
return trie.get(key);
}
@Override
public Map> groupBy(Function super Tuple2, ? extends C> classifier) {
return Maps.groupBy(this, this::createFromEntries, classifier);
}
@Override
public Iterator> grouped(long size) {
return Maps.grouped(this, this::createFromEntries, size);
}
@Override
public Tuple2 head() {
if (isEmpty()) {
throw new NoSuchElementException("head of empty HashMap");
} else {
return iterator().next();
}
}
@Override
public HashMap init() {
if (trie.isEmpty()) {
throw new UnsupportedOperationException("init of empty HashMap");
} else {
return remove(last()._1);
}
}
@Override
public Option> initOption() {
return Maps.initOption(this);
}
@Override
public boolean isEmpty() {
return trie.isEmpty();
}
@Override
public Iterator> iterator() {
return trie.iterator();
}
@Override
public Set keySet() {
return HashSet.ofAll(iterator().map(Tuple2::_1));
}
@Override
public HashMap map(BiFunction super K, ? super V, Tuple2> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
return foldLeft(HashMap.empty(), (acc, entry) -> acc.put(entry.map(mapper)));
}
@Override
public HashMap mapValues(Function super V, ? extends V2> valueMapper) {
Objects.requireNonNull(valueMapper, "valueMapper is null");
return map((k, v) -> Tuple.of(k, valueMapper.apply(v)));
}
@Override
public HashMap merge(Map extends K, ? extends V> that) {
return Maps.merge(this, this::createFromEntries, that);
}
@Override
public HashMap merge(Map extends K, U> that,
BiFunction super V, ? super U, ? extends V> collisionResolution) {
return Maps.merge(this, this::createFromEntries, that, collisionResolution);
}
@Override
public Tuple2, HashMap> partition(Predicate super Tuple2> predicate) {
return Maps.partition(this, this::createFromEntries, predicate);
}
@Override
public HashMap peek(Consumer super Tuple2> action) {
return Maps.peek(this, action);
}
@Override
public HashMap put(K key, V value) {
return new HashMap<>(trie.put(key, value));
}
@Override
public HashMap put(Tuple2 extends K, ? extends V> entry) {
return Maps.put(this, entry);
}
@Override
public HashMap remove(K key) {
final HashArrayMappedTrie result = trie.remove(key);
return result.size() == trie.size() ? this : wrap(result);
}
@Override
public HashMap removeAll(Iterable extends K> keys) {
Objects.requireNonNull(keys, "keys is null");
HashArrayMappedTrie result = trie;
for (K key : keys) {
result = result.remove(key);
}
return result.size() == trie.size() ? this : wrap(result);
}
@Override
public HashMap replace(Tuple2 currentElement, Tuple2 newElement) {
return Maps.replace(this, currentElement, newElement);
}
@Override
public HashMap replaceAll(Tuple2 currentElement, Tuple2 newElement) {
return Maps.replaceAll(this, currentElement, newElement);
}
@Override
public HashMap retainAll(Iterable extends Tuple2> elements) {
Objects.requireNonNull(elements, "elements is null");
HashArrayMappedTrie tree = HashArrayMappedTrie.empty();
for (Tuple2 entry : elements) {
if (contains(entry)) {
tree = tree.put(entry._1, entry._2);
}
}
return wrap(tree);
}
@Override
public HashMap scan(
Tuple2 zero,
BiFunction super Tuple2, ? super Tuple2, ? extends Tuple2> operation) {
return Maps.scan(this, HashMap::empty, zero, operation);
}
@Override
public int size() {
return trie.size();
}
@Override
public Iterator> sliding(long size) {
return Maps.sliding(this, this::createFromEntries, size);
}
@Override
public Iterator> sliding(long size, long step) {
return Maps.sliding(this, this::createFromEntries, size, step);
}
@Override
public Tuple2, HashMap> span(Predicate super Tuple2> predicate) {
return Maps.span(this, this::createFromEntries, predicate);
}
@Override
public HashMap tail() {
if (trie.isEmpty()) {
throw new UnsupportedOperationException("tail of empty HashMap");
} else {
return remove(head()._1);
}
}
@Override
public Option> tailOption() {
return Maps.tailOption(this);
}
@Override
public HashMap take(long n) {
return Maps.take(this, this::createFromEntries, n);
}
@Override
public HashMap takeRight(long n) {
return Maps.takeRight(this, this::createFromEntries, n);
}
@Override
public HashMap takeUntil(Predicate super Tuple2> predicate) {
return Maps.takeUntil(this, this::createFromEntries, predicate);
}
@Override
public HashMap takeWhile(Predicate super Tuple2> predicate) {
return Maps.takeWhile(this, this::createFromEntries, predicate);
}
@Override
public java.util.HashMap toJavaMap() {
return toJavaMap(java.util.HashMap::new, t -> t);
}
@Override
public Seq values() {
return map(Tuple2::_2);
}
@Override
public int hashCode() {
return trie.hashCode();
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof HashMap) {
final HashMap, ?> that = (HashMap, ?>) o;
return this.trie.equals(that.trie);
} else {
return false;
}
}
private Object readResolve() {
return isEmpty() ? EMPTY : this;
}
@Override
public String stringPrefix() {
return "HashMap";
}
@Override
public String toString() {
return mkString(stringPrefix() + "(", ", ", ")");
}
private static HashMap wrap(HashArrayMappedTrie trie) {
return trie.isEmpty() ? empty() : new HashMap<>(trie);
}
// We need this method to narrow the argument of `ofEntries`.
// If this method is static with type args , the jdk fails to infer types at the call site.
private HashMap createFromEntries(Iterable> tuples) {
return HashMap.ofEntries(tuples);
}
}