package io.vavr.collection; import io.vavr.Tuple; import io.vavr.Tuple2; import org.assertj.core.api.Assertions; import org.junit.Test; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Spliterator; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collector; import java.util.stream.Stream; public class LinkedHashMapTest extends AbstractMapTest { @Override protected String className() { return "LinkedHashMap"; } @Override <T1, T2> java.util.Map<T1, T2> javaEmptyMap() { return new java.util.LinkedHashMap<>(); } @Override protected <T1 extends Comparable<? super T1>, T2> LinkedHashMap<T1, T2> emptyMap() { return LinkedHashMap.empty(); } @Override protected <T> Collector<Tuple2<Integer, T>, ArrayList<Tuple2<Integer, T>>, ? extends Map<Integer, T>> mapCollector() { return LinkedHashMap.collector(); } @SuppressWarnings("varargs") @SafeVarargs @Override protected final <K extends Comparable<? super K>, V> LinkedHashMap<K, V> mapOfTuples(Tuple2<? extends K, ? extends V>... entries) { return LinkedHashMap.ofEntries(entries); } @SuppressWarnings("varargs") @SafeVarargs @Override protected final <K extends Comparable<? super K>, V> LinkedHashMap<K, V> mapOfEntries(java.util.Map.Entry<? extends K, ? extends V>... entries) { return LinkedHashMap.ofEntries(entries); } @Override protected <K extends Comparable<? super K>, V> LinkedHashMap<K, V> mapOf(K k1, V v1) { return LinkedHashMap.of(k1, v1); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2) { return LinkedHashMap.of(k1, v1, k2, v2); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2, K k3, V v3) { return LinkedHashMap.of(k1, v1, k2, v2, k3, v3); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { return LinkedHashMap.of(k1, v1, k2, v2, k3, v3, k4, v4); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { return LinkedHashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOf(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5, K k6, V v6) { return LinkedHashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOf(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) { return LinkedHashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOf(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) { return LinkedHashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOf(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) { return LinkedHashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOf(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) { return LinkedHashMap.of(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10); } @Override protected <T, K extends Comparable<? super K>, V> Map<K, V> mapOf(Stream<? extends T> stream, Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends V> valueMapper) { return LinkedHashMap.ofAll(stream, keyMapper, valueMapper); } @Override protected <T, K extends Comparable<? super K>, V> Map<K, V> mapOf(Stream<? extends T> stream, Function<? super T, Tuple2<? extends K, ? extends V>> f) { return LinkedHashMap.ofAll(stream, f); } protected <K extends Comparable<? super K>, V> Map<K, V> mapOfNullKey(K k1, V v1, K k2, V v2) { return mapOf(k1, v1, k2, v2); } @Override protected <K extends Comparable<? super K>, V> Map<K, V> mapOfNullKey(K k1, V v1, K k2, V v2, K k3, V v3) { return mapOf(k1, v1, k2, v2, k3, v3); } @Override protected <K extends Comparable<? super K>, V> LinkedHashMap<K, V> mapTabulate(int n, Function<? super Integer, ? extends Tuple2<? extends K, ? extends V>> f) { return LinkedHashMap.tabulate(n, f); } @Override protected <K extends Comparable<? super K>, V> LinkedHashMap<K, V> mapFill(int n, Supplier<? extends Tuple2<? extends K, ? extends V>> s) { return LinkedHashMap.fill(n, s); } @Test public void shouldKeepOrder() { final List<Character> actual = LinkedHashMap.<Integer, Character> empty().put(3, 'a').put(2, 'b').put(1, 'c').foldLeft(List.empty(), (s, t) -> s.append(t._2)); Assertions.assertThat(actual).isEqualTo(List.of('a', 'b', 'c')); } @Test public void shouldKeepValuesOrder() { final List<Character> actual = LinkedHashMap.<Integer, Character> empty().put(3, 'a').put(2, 'b').put(1, 'c').values().foldLeft(List.empty(), List::append); Assertions.assertThat(actual).isEqualTo(List.of('a', 'b', 'c')); } // -- static narrow @Test public void shouldNarrowLinkedHashMap() { final LinkedHashMap<Integer, Double> int2doubleMap = mapOf(1, 1.0d); final LinkedHashMap<Number, Number> number2numberMap = LinkedHashMap.narrow(int2doubleMap); final int actual = number2numberMap.put(new BigDecimal("2"), new BigDecimal("2.0")).values().sum().intValue(); assertThat(actual).isEqualTo(3); } // -- static ofAll(Iterable) @Test public void shouldWrapMap() { final java.util.Map<Integer, Integer> source = new java.util.HashMap<>(); source.put(1, 2); source.put(3, 4); assertThat(LinkedHashMap.ofAll(source)).isEqualTo(emptyIntInt().put(1, 2).put(3, 4)); } // -- keySet @Test public void shouldKeepKeySetOrder() { final Set<Integer> keySet = LinkedHashMap.of(4, "d", 1, "a", 2, "b").keySet(); assertThat(keySet.mkString()).isEqualTo("412"); } // -- map @Test public void shouldReturnModifiedKeysMapWithNonUniqueMapperAndPredictableOrder() { final Map<Integer, String> actual = LinkedHashMap.of(3, "3").put(1, "1").put(2, "2") .mapKeys(Integer::toHexString).mapKeys(String::length); final Map<Integer, String> expected = LinkedHashMap.of(1, "2"); assertThat(actual).isEqualTo(expected); } // -- put @Test public void shouldKeepOrderWhenPuttingAnExistingKeyAndNonExistingValue() { final Map<Integer, String> map = mapOf(1, "a", 2, "b", 3, "c"); final Map<Integer, String> actual = map.put(1, "d"); final Map<Integer, String> expected = mapOf(1, "d", 2, "b", 3, "c"); assertThat(actual.toList()).isEqualTo(expected.toList()); } @Test public void shouldKeepOrderWhenPuttingAnExistingKeyAndExistingValue() { final Map<Integer, String> map = mapOf(1, "a", 2, "b", 3, "c"); final Map<Integer, String> actual = map.put(1, "a"); final Map<Integer, String> expected = mapOf(1, "a", 2, "b", 3, "c"); assertThat(actual.toList()).isEqualTo(expected.toList()); } // -- replace @Test public void shouldReturnSameInstanceIfReplacingNonExistingPairUsingNonExistingKey() { final Map<Integer, String> map = LinkedHashMap.of(1, "a", 2, "b"); final Map<Integer, String> actual = map.replace(Tuple.of(0, "?"), Tuple.of(0, "!")); assertThat(actual).isSameAs(map); } @Test public void shouldReturnSameInstanceIfReplacingNonExistingPairUsingExistingKey() { final Map<Integer, String> map = LinkedHashMap.of(1, "a", 2, "b"); final Map<Integer, String> actual = map.replace(Tuple.of(2, "?"), Tuple.of(2, "!")); assertThat(actual).isSameAs(map); } @Test public void shouldPreserveOrderWhenReplacingExistingPairWithSameKeyAndDifferentValue() { final Map<Integer, String> map = LinkedHashMap.of(1, "a", 2, "b", 3, "c"); final Map<Integer, String> actual = map.replace(Tuple.of(2, "b"), Tuple.of(2, "B")); final Map<Integer, String> expected = LinkedHashMap.of(1, "a", 2, "B", 3, "c"); assertThat(actual).isEqualTo(expected); Assertions.assertThat(List.ofAll(actual)).isEqualTo(List.ofAll(expected)); } @Test public void shouldPreserveOrderWhenReplacingExistingPairWithDifferentKeyValue() { final Map<Integer, String> map = LinkedHashMap.of(1, "a", 2, "b", 3, "c"); final Map<Integer, String> actual = map.replace(Tuple.of(2, "b"), Tuple.of(4, "B")); final Map<Integer, String> expected = LinkedHashMap.of(1, "a", 4, "B", 3, "c"); assertThat(actual).isEqualTo(expected); Assertions.assertThat(List.ofAll(actual)).isEqualTo(List.ofAll(expected)); } @Test public void shouldPreserveOrderWhenReplacingExistingPairAndRemoveOtherIfKeyAlreadyExists() { final Map<Integer, String> map = LinkedHashMap.of(1, "a", 2, "b", 3, "c", 4, "d", 5, "e"); final Map<Integer, String> actual = map.replace(Tuple.of(2, "b"), Tuple.of(4, "B")); final Map<Integer, String> expected = LinkedHashMap.of(1, "a", 4, "B", 3, "c", 5, "e"); assertThat(actual).isEqualTo(expected); Assertions.assertThat(List.ofAll(actual)).isEqualTo(List.ofAll(expected)); } @Test public void shouldReturnSameInstanceWhenReplacingExistingPairWithIdentity() { final Map<Integer, String> map = LinkedHashMap.of(1, "a", 2, "b", 3, "c"); final Map<Integer, String> actual = map.replace(Tuple.of(2, "b"), Tuple.of(2, "b")); assertThat(actual).isSameAs(map); } // -- scan, scanLeft, scanRight @Test public void shouldScan() { final Map<Integer, String> map = this.<Integer, String> emptyMap() .put(Tuple.of(1, "a")) .put(Tuple.of(2, "b")) .put(Tuple.of(3, "c")) .put(Tuple.of(4, "d")); final Map<Integer, String> result = map.scan(Tuple.of(0, "x"), (t1, t2) -> Tuple.of(t1._1 + t2._1, t1._2 + t2._2)); assertThat(result).isEqualTo(LinkedHashMap.empty() .put(0, "x") .put(1, "xa") .put(3, "xab") .put(6, "xabc") .put(10, "xabcd")); } @Test public void shouldScanLeft() { final Map<Integer, String> map = this.<Integer, String> emptyMap() .put(Tuple.of(1, "a")) .put(Tuple.of(2, "b")) .put(Tuple.of(3, "c")) .put(Tuple.of(4, "d")); final Seq<Tuple2<Integer, String>> result = map.scanLeft(Tuple.of(0, "x"), (t1, t2) -> Tuple.of(t1._1 + t2._1, t1._2 + t2._2)); assertThat(result).isEqualTo(List.of( Tuple.of(0, "x"), Tuple.of(1, "xa"), Tuple.of(3, "xab"), Tuple.of(6, "xabc"), Tuple.of(10, "xabcd"))); } @Test public void shouldScanRight() { final Map<Integer, String> map = this.<Integer, String> emptyMap() .put(Tuple.of(1, "a")) .put(Tuple.of(2, "b")) .put(Tuple.of(3, "c")) .put(Tuple.of(4, "d")); final Seq<Tuple2<Integer, String>> result = map.scanRight(Tuple.of(0, "x"), (t1, t2) -> Tuple.of(t1._1 + t2._1, t1._2 + t2._2)); assertThat(result).isEqualTo(List.of( Tuple.of(10, "abcdx"), Tuple.of(9, "bcdx"), Tuple.of(7, "cdx"), Tuple.of(4, "dx"), Tuple.of(0, "x"))); } // -- spliterator @Test public void shouldNotHaveSortedSpliterator() { assertThat(of(1, 2, 3).spliterator().hasCharacteristics(Spliterator.SORTED)).isFalse(); } @Test public void shouldHaveOrderedSpliterator() { assertThat(of(1, 2, 3).spliterator().hasCharacteristics(Spliterator.ORDERED)).isTrue(); } // -- isSequential() @Test public void shouldReturnTrueWhenIsSequentialCalled() { assertThat(LinkedHashMap.of(1, 2, 3, 4).isSequential()).isTrue(); } }