javaslang.collection.LinkedHashMap Maven / Gradle / Ivy
/* / \____ _ _ ____ ______ / \ ____ __ _______
* / / \/ \ / \/ \ / /\__\/ // \/ \ // /\__\ 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.Objects;
import java.util.function.*;
import java.util.stream.Collector;
/**
* An immutable {@code LinkedHashMap} implementation.
*
* @author Ruslan Sennov
* @since 2.0.0
*/
public final class LinkedHashMap implements Kind2, K, V>, Map, Serializable {
private static final long serialVersionUID = 1L;
private static final LinkedHashMap, ?> EMPTY = new LinkedHashMap<>(Queue.empty(), HashMap.empty());
private final Queue> list;
private final HashMap map;
private LinkedHashMap(Queue> list, HashMap map) {
this.list = list;
this.map = map;
}
/**
* 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.LinkedHashMap}.
*
* @param The key type
* @param The value type
* @return A {@link javaslang.collection.LinkedHashMap} Collector.
*/
public static Collector, ArrayList>, LinkedHashMap> collector() {
final Supplier>> supplier = ArrayList::new;
final BiConsumer>, Tuple2> accumulator = ArrayList::add;
final BinaryOperator>> combiner = (left, right) -> {
left.addAll(right);
return left;
};
final Function>, LinkedHashMap> finisher = LinkedHashMap::ofEntries;
return Collector.of(supplier, accumulator, combiner, finisher);
}
@SuppressWarnings("unchecked")
public static LinkedHashMap empty() {
return (LinkedHashMap) EMPTY;
}
/**
* Narrows a widened {@code LinkedHashMap extends K, ? extends V>} to {@code LinkedHashMap}
* by performing a type safe-cast. This is eligible because immutable/read-only
* collections are covariant.
*
* @param linkedHashMap A {@code LinkedHashMap}.
* @param Key type
* @param Value type
* @return the given {@code linkedHashMap} instance as narrowed type {@code LinkedHashMap}.
*/
@SuppressWarnings("unchecked")
public static LinkedHashMap narrow(LinkedHashMap extends K, ? extends V> linkedHashMap) {
return (LinkedHashMap) linkedHashMap;
}
/**
* Returns a singleton {@code LinkedHashMap}, i.e. a {@code LinkedHashMap} of one element.
*
* @param entry A map entry.
* @param The key type
* @param The value type
* @return A new Map containing the given entry
*/
@SuppressWarnings("unchecked")
public static LinkedHashMap of(Tuple2 extends K, ? extends V> entry) {
final HashMap map = HashMap.of(entry);
final Queue> list = Queue.of((Tuple2) entry);
return new LinkedHashMap<>(list, map);
}
/**
* Creates a LinkedHashMap 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 LinkedHashMap of(Object... pairs) {
Objects.requireNonNull(pairs, "pairs is null");
if ((pairs.length & 1) != 0) {
throw new IllegalArgumentException("Odd length of key-value pairs list");
}
HashMap map = HashMap.empty();
Queue> list = Queue.empty();
for (int i = 0; i < pairs.length; i += 2) {
final K k = (K) pairs[i];
final V v = (V) pairs[i + 1];
map = map.put(k, v);
list = list.append(Tuple.of(k, v));
}
return wrap(list, map);
}
/**
* Returns a {@code LinkedHashMap}, 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 LinkedHashMap ofAll(java.util.Map extends K, ? extends V> map) {
Objects.requireNonNull(map, "map is null");
LinkedHashMap result = LinkedHashMap.empty();
for (java.util.Map.Entry extends K, ? extends V> entry : map.entrySet()) {
result = result.put(entry.getKey(), entry.getValue());
}
return result;
}
/**
* Returns a singleton {@code LinkedHashMap}, i.e. a {@code LinkedHashMap} 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 LinkedHashMap of(K key, V value) {
final HashMap map = HashMap.of(key, value);
final Queue> list = Queue.of(Tuple.of(key, value));
return new LinkedHashMap<>(list, map);
}
/**
* Returns a LinkedHashMap 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 LinkedHashMap
* @param f The Function computing element values
* @return A LinkedHashMap consisting of elements {@code f(0),f(1), ..., f(n - 1)}
* @throws NullPointerException if {@code f} is null
*/
@SuppressWarnings("unchecked")
public static LinkedHashMap 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 a LinkedHashMap 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 LinkedHashMap
* @param s The Supplier computing element values
* @return A LinkedHashMap 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 LinkedHashMap 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 LinkedHashMap 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 LinkedHashMap ofEntries(java.util.Map.Entry extends K, ? extends V>... entries) {
HashMap map = HashMap.empty();
Queue> list = Queue.empty();
for (java.util.Map.Entry extends K, ? extends V> entry : entries) {
final Tuple2 tuple = Tuple.of(entry.getKey(), entry.getValue());
map = map.put(tuple);
list = list.append(tuple);
}
return wrap(list, map);
}
/**
* Creates a LinkedHashMap 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 LinkedHashMap ofEntries(Tuple2 extends K, ? extends V>... entries) {
final HashMap map = HashMap.ofEntries(entries);
final Queue> list = Queue.of((Tuple2[]) entries);
return wrap(list, map);
}
/**
* Creates a LinkedHashMap 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 LinkedHashMap ofEntries(Iterable extends Tuple2 extends K, ? extends V>> entries) {
Objects.requireNonNull(entries, "entries is null");
if (entries instanceof LinkedHashMap) {
return (LinkedHashMap) entries;
} else {
HashMap map = HashMap.empty();
Queue> list = Queue.empty();
for (Tuple2 extends K, ? extends V> entry : entries) {
map = map.put(entry);
list = list.append((Tuple2) entry);
}
return wrap(list, map);
}
}
@Override
public LinkedHashMap 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 LinkedHashMap.ofEntries(entries);
}
@Override
public boolean containsKey(K key) {
return map.containsKey(key);
}
@Override
public LinkedHashMap distinct() {
return Maps.distinct(this);
}
@Override
public LinkedHashMap distinctBy(Comparator super Tuple2> comparator) {
return Maps.distinctBy(this, this::createFromEntries, comparator);
}
@Override
public LinkedHashMap distinctBy(Function super Tuple2, ? extends U> keyExtractor) {
return Maps.distinctBy(this, this::createFromEntries, keyExtractor);
}
@Override
public LinkedHashMap drop(long n) {
return Maps.drop(this, this::createFromEntries, LinkedHashMap::empty, n);
}
@Override
public LinkedHashMap dropRight(long n) {
return Maps.dropRight(this, this::createFromEntries, LinkedHashMap::empty, n);
}
@Override
public LinkedHashMap dropUntil(Predicate super Tuple2> predicate) {
return Maps.dropUntil(this, this::createFromEntries, predicate);
}
@Override
public LinkedHashMap dropWhile(Predicate super Tuple2> predicate) {
return Maps.dropWhile(this, this::createFromEntries, predicate);
}
@Override
public LinkedHashMap filter(Predicate super Tuple2> predicate) {
return Maps.filter(this, this::createFromEntries, predicate);
}
@Override
public LinkedHashMap flatMap(BiFunction super K, ? super V, ? extends Iterable>> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
return foldLeft(LinkedHashMap. 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 map.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() {
return list.head();
}
@Override
public LinkedHashMap init() {
if (isEmpty()) {
throw new UnsupportedOperationException("init of empty LinkedHashMap");
} else {
return LinkedHashMap.ofEntries(list.init());
}
}
@Override
public Option> initOption() {
return Maps.initOption(this);
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public Iterator> iterator() {
return list.iterator();
}
@Override
public Set keySet() {
return map.keySet();
}
@Override
public LinkedHashMap map(BiFunction super K, ? super V, Tuple2> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
return foldLeft(LinkedHashMap.empty(), (acc, entry) -> acc.put(entry.map(mapper)));
}
@Override
public LinkedHashMap mapValues(Function super V, ? extends W> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
return map((k, v) -> Tuple.of(k, mapper.apply(v)));
}
@Override
public LinkedHashMap merge(Map extends K, ? extends V> that) {
return Maps.merge(this, this::createFromEntries, that);
}
@Override
public LinkedHashMap 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, LinkedHashMap> partition(Predicate super Tuple2> predicate) {
return Maps.partition(this, this::createFromEntries, predicate);
}
@Override
public LinkedHashMap peek(Consumer super Tuple2> action) {
return Maps.peek(this, action);
}
@Override
public LinkedHashMap put(K key, V value) {
Queue> newList = list;
HashMap newMap = map;
if (containsKey(key)) {
newList = newList.filter(t -> !Objects.equals(t._1, key));
newMap = newMap.remove(key);
}
newList = newList.append(Tuple.of(key, value));
newMap = newMap.put(key, value);
return new LinkedHashMap<>(newList, newMap);
}
@Override
public LinkedHashMap put(Tuple2 extends K, ? extends V> entry) {
return Maps.put(this, entry);
}
@Override
public LinkedHashMap remove(K key) {
if (containsKey(key)) {
final Queue> newList = list.removeFirst(t -> Objects.equals(t._1, key));
final HashMap newMap = map.remove(key);
return wrap(newList, newMap);
} else {
return this;
}
}
@Override
public LinkedHashMap removeAll(Iterable extends K> keys) {
Objects.requireNonNull(keys, "keys is null");
final HashSet toRemove = HashSet.ofAll(keys);
final Queue> newList = list.filter(t -> !toRemove.contains(t._1));
final HashMap newMap = map.filter(t -> !toRemove.contains(t._1));
return newList.size() == size() ? this : wrap(newList, newMap);
}
@Override
public LinkedHashMap replace(Tuple2 currentElement, Tuple2 newElement) {
Objects.requireNonNull(currentElement, "currentElement is null");
Objects.requireNonNull(newElement, "newElement is null");
// We replace the whole element, i.e. key and value have to be present.
if (!Objects.equals(currentElement, newElement) && contains(currentElement)) {
Queue> newList = list;
HashMap newMap = map;
final K currentKey = currentElement._1;
final K newKey = newElement._1;
// If current key and new key are equal, the element will be automatically replaced,
// otherwise we need to remove the pair (newKey, ?) from the list manually.
if (!Objects.equals(currentKey, newKey)) {
final Option value = newMap.get(newKey);
if (value.isDefined()) {
newList = newList.remove(Tuple.of(newKey, value.get()));
}
}
newList = newList.replace(currentElement, newElement);
newMap = newMap.remove(currentKey).put(newElement);
return wrap(newList, newMap);
} else {
return this;
}
}
@Override
public LinkedHashMap replaceAll(Tuple2 currentElement, Tuple2 newElement) {
return Maps.replaceAll(this, currentElement, newElement);
}
@Override
public LinkedHashMap retainAll(Iterable extends Tuple2> elements) {
Objects.requireNonNull(elements, "elements is null");
LinkedHashMap result = empty();
for (Tuple2 entry : elements) {
if (contains(entry)) {
result = result.put(entry._1, entry._2);
}
}
return result;
}
@Override
public LinkedHashMap scan(
Tuple2 zero,
BiFunction super Tuple2, ? super Tuple2, ? extends Tuple2> operation) {
return Maps.scan(this, LinkedHashMap::empty, zero, operation);
}
@Override
public int size() {
return map.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, LinkedHashMap> span(Predicate super Tuple2> predicate) {
return Maps.span(this, this::createFromEntries, predicate);
}
@Override
public LinkedHashMap tail() {
if (isEmpty()) {
throw new UnsupportedOperationException("tail of empty LinkedHashMap");
} else {
return LinkedHashMap.ofEntries(list.tail());
}
}
@Override
public Option> tailOption() {
return Maps.tailOption(this);
}
@Override
public LinkedHashMap take(long n) {
return Maps.take(this, this::createFromEntries, n);
}
@Override
public LinkedHashMap takeRight(long n) {
return Maps.takeRight(this, this::createFromEntries, n);
}
@Override
public LinkedHashMap takeUntil(Predicate super Tuple2> predicate) {
return Maps.takeUntil(this, this::createFromEntries, predicate);
}
@Override
public LinkedHashMap takeWhile(Predicate super Tuple2> predicate) {
return Maps.takeWhile(this, this::createFromEntries, predicate);
}
@Override
public java.util.LinkedHashMap toJavaMap() {
return toJavaMap(java.util.LinkedHashMap::new, t -> t);
}
@Override
public Seq values() {
return map.values();
}
@Override
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof LinkedHashMap) {
final LinkedHashMap, ?> that = (LinkedHashMap, ?>) o;
return this.list.equals(that.list);
} else {
return false;
}
}
@Override
public int hashCode() {
return list.hashCode();
}
private Object readResolve() {
return isEmpty() ? EMPTY : this;
}
@Override
public String stringPrefix() {
return "LinkedHashMap";
}
@Override
public String toString() {
return mkString(stringPrefix() + "(", ", ", ")");
}
private static LinkedHashMap wrap(Queue> list, HashMap map) {
return list.isEmpty() ? empty() : new LinkedHashMap<>(list, map);
}
// 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 LinkedHashMap createFromEntries(Iterable> tuples) {
return LinkedHashMap.ofEntries(tuples);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy