All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vavr.collection.LinkedHashMap Maven / Gradle / Ivy

There is a newer version: 1.0.0-alpha-4
Show newest version
/*  __    __  __  __    __  ___
 * \  \  /  /    \  \  /  /  __/
 *  \  \/  /  /\  \  \/  /  /
 *   \____/__/  \__\____/__/
 *
 * Copyright 2014-2019 Vavr, http://vavr.io
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.vavr.collection;

import io.vavr.Tuple;
import io.vavr.Tuple2;
import io.vavr.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 that has predictable (insertion-order) iteration.
 *
 * @author Ruslan Sennov
 */
public final class LinkedHashMap implements 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 LinkedHashMap}.
     *
     * @param  The key type
     * @param  The value type
     * @return A {@link 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);
    }

    /**
     * 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 LinkedHashMap}.
     *
     * @param keyMapper The key mapper
     * @param  The key type
     * @param  The value type
     * @param  Initial {@link java.util.stream.Stream} elements type
     * @return A {@link LinkedHashMap} Collector.
     */
    public static  Collector, LinkedHashMap> collector(Function keyMapper) {
        Objects.requireNonNull(keyMapper, "keyMapper is null");
        return LinkedHashMap.collector(keyMapper, v -> v);
    }

    /**
     * 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 LinkedHashMap}.
     *
     * @param keyMapper The key mapper
     * @param valueMapper The value mapper
     * @param  The key type
     * @param  The value type
     * @param  Initial {@link java.util.stream.Stream} elements type
     * @return A {@link LinkedHashMap} Collector.
     */
    public static  Collector, LinkedHashMap> collector(
            Function keyMapper, Function valueMapper) {
        Objects.requireNonNull(keyMapper, "keyMapper is null");
        Objects.requireNonNull(valueMapper, "valueMapper is null");
        final Supplier> supplier = ArrayList::new;
        final BiConsumer, T> accumulator = ArrayList::add;
        final BinaryOperator> combiner = (left, right) -> {
            left.addAll(right);
            return left;
        };
        final Function, LinkedHashMap> finisher = arr -> LinkedHashMap.ofEntries(Iterator.ofAll(arr)
                .map(t -> Tuple.of(keyMapper.apply(t), valueMapper.apply(t))));
        return Collector.of(supplier, accumulator, combiner, finisher);
    }

    @SuppressWarnings("unchecked")
    public static  LinkedHashMap empty() {
        return (LinkedHashMap) EMPTY;
    }

    /**
     * Narrows a widened {@code LinkedHashMap} 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 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 entry) {
        final HashMap map = HashMap.of(entry);
        final Queue> list = Queue.of((Tuple2) entry);
        return wrap(list, map);
    }

    /**
     * Returns a {@code LinkedHashMap}, from a source java.util.Map.
     *
     * @param map A map
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given map
     */
    public static  LinkedHashMap ofAll(java.util.Map map) {
        Objects.requireNonNull(map, "map is null");
        LinkedHashMap result = LinkedHashMap.empty();
        for (java.util.Map.Entry entry : map.entrySet()) {
            result = result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }

    /**
     * Returns a {@code LinkedHashMap}, from entries mapped from stream.
     *
     * @param stream      the source stream
     * @param entryMapper the entry mapper
     * @param          The stream element type
     * @param          The key type
     * @param          The value type
     * @return A new Map
     */
    public static  LinkedHashMap ofAll(java.util.stream.Stream stream,
            Function> entryMapper) {
        return Maps.ofStream(empty(), stream, entryMapper);
    }

    /**
     * Returns a {@code LinkedHashMap}, from entries mapped from stream.
     *
     * @param stream      the source stream
     * @param keyMapper   the key mapper
     * @param valueMapper the value mapper
     * @param          The stream element type
     * @param          The key type
     * @param          The value type
     * @return A new Map
     */
    public static  LinkedHashMap ofAll(java.util.stream.Stream stream,
            Function keyMapper,
            Function valueMapper) {
        return Maps.ofStream(empty(), stream, keyMapper, valueMapper);
    }

    /**
     * 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 wrap(list, map);
    }

    /**
     * Creates a LinkedHashMap of the given list of key-value pairs.
     *
     * @param k1  a key for the map
     * @param v1  the value for k1
     * @param k2  a key for the map
     * @param v2  the value for k2
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given entries
     */
    public static  LinkedHashMap of(K k1, V v1, K k2, V v2) {
        final HashMap map = HashMap.of(k1, v1, k2, v2);
        final Queue> list = Queue.of(Tuple.of(k1, v1), Tuple.of(k2, v2));
        return wrapNonUnique(list, map);
    }

    /**
     * Creates a LinkedHashMap of the given list of key-value pairs.
     *
     * @param k1  a key for the map
     * @param v1  the value for k1
     * @param k2  a key for the map
     * @param v2  the value for k2
     * @param k3  a key for the map
     * @param v3  the value for k3
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given entries
     */
    public static  LinkedHashMap of(K k1, V v1, K k2, V v2, K k3, V v3) {
        final HashMap map = HashMap.of(k1, v1, k2, v2, k3, v3);
        final Queue> list = Queue.of(Tuple.of(k1, v1), Tuple.of(k2, v2), Tuple.of(k3, v3));
        return wrapNonUnique(list, map);
    }

    /**
     * Creates a LinkedHashMap of the given list of key-value pairs.
     *
     * @param k1  a key for the map
     * @param v1  the value for k1
     * @param k2  a key for the map
     * @param v2  the value for k2
     * @param k3  a key for the map
     * @param v3  the value for k3
     * @param k4  a key for the map
     * @param v4  the value for k4
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given entries
     */
    public static  LinkedHashMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
        final HashMap map = HashMap.of(k1, v1, k2, v2, k3, v3, k4, v4);
        final Queue> list = Queue.of(Tuple.of(k1, v1), Tuple.of(k2, v2), Tuple.of(k3, v3), Tuple.of(k4, v4));
        return wrapNonUnique(list, map);
    }

    /**
     * Creates a LinkedHashMap of the given list of key-value pairs.
     *
     * @param k1  a key for the map
     * @param v1  the value for k1
     * @param k2  a key for the map
     * @param v2  the value for k2
     * @param k3  a key for the map
     * @param v3  the value for k3
     * @param k4  a key for the map
     * @param v4  the value for k4
     * @param k5  a key for the map
     * @param v5  the value for k5
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given entries
     */
    public static  LinkedHashMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
        final HashMap map = HashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5);
        final Queue> list = Queue.of(Tuple.of(k1, v1), Tuple.of(k2, v2), Tuple.of(k3, v3), Tuple.of(k4, v4), Tuple.of(k5, v5));
        return wrapNonUnique(list, map);
    }

    /**
     * Creates a LinkedHashMap of the given list of key-value pairs.
     *
     * @param k1  a key for the map
     * @param v1  the value for k1
     * @param k2  a key for the map
     * @param v2  the value for k2
     * @param k3  a key for the map
     * @param v3  the value for k3
     * @param k4  a key for the map
     * @param v4  the value for k4
     * @param k5  a key for the map
     * @param v5  the value for k5
     * @param k6  a key for the map
     * @param v6  the value for k6
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given entries
     */
    public static  LinkedHashMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) {
        final HashMap map = HashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6);
        final Queue> list = Queue.of(Tuple.of(k1, v1), Tuple.of(k2, v2), Tuple.of(k3, v3), Tuple.of(k4, v4), Tuple.of(k5, v5), Tuple.of(k6, v6));
        return wrapNonUnique(list, map);
    }

    /**
     * Creates a LinkedHashMap of the given list of key-value pairs.
     *
     * @param k1  a key for the map
     * @param v1  the value for k1
     * @param k2  a key for the map
     * @param v2  the value for k2
     * @param k3  a key for the map
     * @param v3  the value for k3
     * @param k4  a key for the map
     * @param v4  the value for k4
     * @param k5  a key for the map
     * @param v5  the value for k5
     * @param k6  a key for the map
     * @param v6  the value for k6
     * @param k7  a key for the map
     * @param v7  the value for k7
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given entries
     */
    public static  LinkedHashMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7) {
        final HashMap map = HashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7);
        final Queue> list = Queue.of(Tuple.of(k1, v1), Tuple.of(k2, v2), Tuple.of(k3, v3), Tuple.of(k4, v4), Tuple.of(k5, v5), Tuple.of(k6, v6), Tuple.of(k7, v7));
        return wrapNonUnique(list, map);
    }

    /**
     * Creates a LinkedHashMap of the given list of key-value pairs.
     *
     * @param k1  a key for the map
     * @param v1  the value for k1
     * @param k2  a key for the map
     * @param v2  the value for k2
     * @param k3  a key for the map
     * @param v3  the value for k3
     * @param k4  a key for the map
     * @param v4  the value for k4
     * @param k5  a key for the map
     * @param v5  the value for k5
     * @param k6  a key for the map
     * @param v6  the value for k6
     * @param k7  a key for the map
     * @param v7  the value for k7
     * @param k8  a key for the map
     * @param v8  the value for k8
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given entries
     */
    public static  LinkedHashMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8) {
        final HashMap map = HashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8);
        final Queue> list = Queue.of(Tuple.of(k1, v1), Tuple.of(k2, v2), Tuple.of(k3, v3), Tuple.of(k4, v4), Tuple.of(k5, v5), Tuple.of(k6, v6), Tuple.of(k7, v7), Tuple.of(k8, v8));
        return wrapNonUnique(list, map);
    }

    /**
     * Creates a LinkedHashMap of the given list of key-value pairs.
     *
     * @param k1  a key for the map
     * @param v1  the value for k1
     * @param k2  a key for the map
     * @param v2  the value for k2
     * @param k3  a key for the map
     * @param v3  the value for k3
     * @param k4  a key for the map
     * @param v4  the value for k4
     * @param k5  a key for the map
     * @param v5  the value for k5
     * @param k6  a key for the map
     * @param v6  the value for k6
     * @param k7  a key for the map
     * @param v7  the value for k7
     * @param k8  a key for the map
     * @param v8  the value for k8
     * @param k9  a key for the map
     * @param v9  the value for k9
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given entries
     */
    public static  LinkedHashMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9) {
        final HashMap map = HashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9);
        final Queue> list = Queue.of(Tuple.of(k1, v1), Tuple.of(k2, v2), Tuple.of(k3, v3), Tuple.of(k4, v4), Tuple.of(k5, v5), Tuple.of(k6, v6), Tuple.of(k7, v7), Tuple.of(k8, v8), Tuple.of(k9, v9));
        return wrapNonUnique(list, map);
    }

    /**
     * Creates a LinkedHashMap of the given list of key-value pairs.
     *
     * @param k1  a key for the map
     * @param v1  the value for k1
     * @param k2  a key for the map
     * @param v2  the value for k2
     * @param k3  a key for the map
     * @param v3  the value for k3
     * @param k4  a key for the map
     * @param v4  the value for k4
     * @param k5  a key for the map
     * @param v5  the value for k5
     * @param k6  a key for the map
     * @param v6  the value for k6
     * @param k7  a key for the map
     * @param v7  the value for k7
     * @param k8  a key for the map
     * @param v8  the value for k8
     * @param k9  a key for the map
     * @param v9  the value for k9
     * @param k10 a key for the map
     * @param v10 the value for k10
     * @param  The key type
     * @param  The value type
     * @return A new Map containing the given entries
     */
    public static  LinkedHashMap of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6, K k7, V v7, K k8, V v8, K k9, V v9, K k10, V v10) {
        final HashMap map = HashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10);
        final Queue> list = Queue.of(Tuple.of(k1, v1), Tuple.of(k2, v2), Tuple.of(k3, v3), Tuple.of(k4, v4), Tuple.of(k5, v5), Tuple.of(k6, v6), Tuple.of(k7, v7), Tuple.of(k8, v8), Tuple.of(k9, v9), Tuple.of(k10, v10));
        return wrapNonUnique(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> f) {
        Objects.requireNonNull(f, "f is null");
        return ofEntries(Collections.tabulate(n, (Function>) f));
    }

    /**
     * Returns a LinkedHashMap containing tuples returned by {@code n} calls to 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> s) {
        Objects.requireNonNull(s, "s is null");
        return ofEntries(Collections.fill(n, (Supplier>) 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... entries) {
        HashMap map = HashMap.empty();
        Queue> list = Queue.empty();
        for (java.util.Map.Entry entry : entries) {
            final Tuple2 tuple = Tuple.of(entry.getKey(), entry.getValue());
            map = map.put(tuple);
            list = list.append(tuple);
        }
        return wrapNonUnique(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... entries) {
        final HashMap map = HashMap.ofEntries(entries);
        final Queue> list = Queue.of((Tuple2[]) entries);
        return wrapNonUnique(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> 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 entry : entries) {
                map = map.put(entry);
                list = list.append((Tuple2) entry);
            }
            return wrapNonUnique(list, map);
        }
    }

    @Override
    public  LinkedHashMap bimap(Function keyMapper, Function 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 Tuple2> computeIfAbsent(K key, Function mappingFunction) {
        return Maps.computeIfAbsent(this, key, mappingFunction);
    }

    @Override
    public Tuple2, LinkedHashMap> computeIfPresent(K key, BiFunction remappingFunction) {
        return Maps.computeIfPresent(this, key, remappingFunction);
    }

    @Override
    public boolean containsKey(K key) {
        return map.containsKey(key);
    }

    @Override
    public LinkedHashMap distinct() {
        return Maps.distinct(this);
    }

    @Override
    public LinkedHashMap distinctBy(Comparator> comparator) {
        return Maps.distinctBy(this, this::createFromEntries, comparator);
    }

    @Override
    public  LinkedHashMap distinctBy(Function, ? extends U> keyExtractor) {
        return Maps.distinctBy(this, this::createFromEntries, keyExtractor);
    }

    @Override
    public LinkedHashMap drop(int n) {
        return Maps.drop(this, this::createFromEntries, LinkedHashMap::empty, n);
    }

    @Override
    public LinkedHashMap dropRight(int n) {
        return Maps.dropRight(this, this::createFromEntries, LinkedHashMap::empty, n);
    }

    @Override
    public LinkedHashMap dropUntil(Predicate> predicate) {
        return Maps.dropUntil(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap dropWhile(Predicate> predicate) {
        return Maps.dropWhile(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap filter(BiPredicate predicate) {
        return Maps.filter(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap reject(BiPredicate predicate) {
        return Maps.reject(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap filter(Predicate> predicate) {
        return Maps.filter(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap reject(Predicate> predicate) {
        return Maps.reject(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap filterKeys(Predicate predicate) {
        return Maps.filterKeys(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap rejectKeys(Predicate predicate) {
        return Maps.rejectKeys(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap filterValues(Predicate predicate) {
        return Maps.filterValues(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap rejectValues(Predicate predicate) {
        return Maps.rejectValues(this, this::createFromEntries, predicate);
    }

    @Override
    public  LinkedHashMap flatMap(BiFunction>> mapper) {
        Objects.requireNonNull(mapper, "mapper is null");
        return foldLeft(LinkedHashMap. empty(), (acc, entry) -> {
            for (Tuple2 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 V getOrElse(K key, V defaultValue) {
        return map.getOrElse(key, defaultValue);
    }

    @Override
    public  Map> groupBy(Function, ? extends C> classifier) {
        return Maps.groupBy(this, this::createFromEntries, classifier);
    }

    @Override
    public Iterator> grouped(int 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);
    }

    /**
     * An {@code LinkedHashMap}'s value is computed synchronously.
     *
     * @return false
     */
    @Override
    public boolean isAsync() {
        return false;
    }

    @Override
    public boolean isEmpty() {
        return map.isEmpty();
    }

    /**
     * An {@code LinkedHashMap}'s value is computed eagerly.
     *
     * @return false
     */
    @Override
    public boolean isLazy() {
        return false;
    }

    @Override
    public boolean isSequential() {
        return true;
    }

    @Override
    public Iterator> iterator() {
        return list.iterator();
    }

    @SuppressWarnings("unchecked")
    @Override
    public Set keySet() {
        return LinkedHashSet.wrap((LinkedHashMap) this);
    }

    @Override
    public Tuple2 last() {
        return list.last();
    }

    @Override
    public  LinkedHashMap map(BiFunction> mapper) {
        Objects.requireNonNull(mapper, "mapper is null");
        return foldLeft(LinkedHashMap.empty(), (acc, entry) -> acc.put(entry.map(mapper)));
    }

    @Override
    public  LinkedHashMap mapKeys(Function keyMapper) {
        Objects.requireNonNull(keyMapper, "keyMapper is null");
        return map((k, v) -> Tuple.of(keyMapper.apply(k), v));
    }

    @Override
    public  LinkedHashMap mapKeys(Function keyMapper, BiFunction valueMerge) {
        return Collections.mapKeys(this, LinkedHashMap.empty(), keyMapper, valueMerge);
    }

    @Override
    public  LinkedHashMap mapValues(Function mapper) {
        Objects.requireNonNull(mapper, "mapper is null");
        return map((k, v) -> Tuple.of(k, mapper.apply(v)));
    }

    @Override
    public LinkedHashMap merge(Map that) {
        return Maps.merge(this, this::createFromEntries, that);
    }

    @Override
    public  LinkedHashMap merge(Map that,
            BiFunction collisionResolution) {
        return Maps.merge(this, this::createFromEntries, that, collisionResolution);
    }

    @Override
    public LinkedHashMap orElse(Iterable> other) {
        return isEmpty() ? ofEntries(other) : this;
    }

    @Override
    public LinkedHashMap orElse(Supplier>> supplier) {
        return isEmpty() ? ofEntries(supplier.get()) : this;
    }

    @Override
    public Tuple2, LinkedHashMap> partition(Predicate> predicate) {
        return Maps.partition(this, this::createFromEntries, predicate);
    }

    @Override
    public LinkedHashMap peek(Consumer> action) {
        return Maps.peek(this, action);
    }

    @Override
    public  LinkedHashMap put(K key, U value, BiFunction merge) {
        return Maps.put(this, key, value, merge);
    }

    /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old value is
     * replaced by the specified value.
     * 

* Note that this method has a worst-case linear complexity. * * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return A new Map containing these elements and that entry. */ @Override public LinkedHashMap put(K key, V value) { final Queue> newList; final Option currentEntry = get(key); if (currentEntry.isDefined()) { newList = list.replace(Tuple.of(key, currentEntry.get()), Tuple.of(key, value)); } else { newList = list.append(Tuple.of(key, value)); } final HashMap newMap = map.put(key, value); return wrap(newList, newMap); } @Override public LinkedHashMap put(Tuple2 entry) { return Maps.put(this, entry); } @Override public LinkedHashMap put(Tuple2 entry, BiFunction merge) { return Maps.put(this, entry, merge); } @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 @Deprecated public LinkedHashMap removeAll(BiPredicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return reject(predicate); } @Override public LinkedHashMap removeAll(Iterable 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 @Deprecated public LinkedHashMap removeKeys(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return rejectKeys(predicate); } @Override @Deprecated public LinkedHashMap removeValues(Predicate predicate) { Objects.requireNonNull(predicate, "predicate is null"); return rejectValues(predicate); } @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 replaceValue(K key, V value) { return Maps.replaceValue(this, key, value); } @Override public LinkedHashMap replace(K key, V oldValue, V newValue) { return Maps.replace(this, key, oldValue, newValue); } @Override public LinkedHashMap replaceAll(BiFunction function) { return Maps.replaceAll(this, function); } @Override public LinkedHashMap retainAll(Iterable> 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, ? extends Tuple2> operation) { return Maps.scan(this, zero, operation, this::createFromEntries); } @Override public int size() { return map.size(); } @Override public Iterator> slideBy(Function, ?> classifier) { return Maps.slideBy(this, this::createFromEntries, classifier); } @Override public Iterator> sliding(int size) { return Maps.sliding(this, this::createFromEntries, size); } @Override public Iterator> sliding(int size, int step) { return Maps.sliding(this, this::createFromEntries, size, step); } @Override public Tuple2, LinkedHashMap> span(Predicate> 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(int n) { return Maps.take(this, this::createFromEntries, n); } @Override public LinkedHashMap takeRight(int n) { return Maps.takeRight(this, this::createFromEntries, n); } @Override public LinkedHashMap takeUntil(Predicate> predicate) { return Maps.takeUntil(this, this::createFromEntries, predicate); } @Override public LinkedHashMap takeWhile(Predicate> 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(t -> t._2); } @Override public boolean equals(Object o) { return Collections.equals(this, o); } @Override public int hashCode() { return Collections.hashUnordered(this); } private Object readResolve() { return isEmpty() ? EMPTY : this; } @Override public String stringPrefix() { return "LinkedHashMap"; } @Override public String toString() { return mkString(stringPrefix() + "(", ", ", ")"); } /** * Construct Map with given values and key order. * * @param list The list of key-value tuples with unique keys. * @param map The map of key-value tuples. * @param The key type * @param The value type * @return A new Map containing the given map with given key order */ private static LinkedHashMap wrap(Queue> list, HashMap map) { return list.isEmpty() ? empty() : new LinkedHashMap<>(list, map); } /** * Construct Map with given values and key order. * * @param list The list of key-value tuples with non-unique keys. * @param map The map of key-value tuples. * @param The key type * @param The value type * @return A new Map containing the given map with given key order */ private static LinkedHashMap wrapNonUnique(Queue> list, HashMap map) { return list.isEmpty() ? empty() : new LinkedHashMap<>(list.reverse().distinctBy(Tuple2::_1).reverse().toQueue(), 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); } }