diff --git a/javaslang/src/main/java/javaslang/collection/AbstractMap.java b/javaslang/src/main/java/javaslang/collection/AbstractMap.java deleted file mode 100644 index 9d23580ead..0000000000 --- a/javaslang/src/main/java/javaslang/collection/AbstractMap.java +++ /dev/null @@ -1,312 +0,0 @@ -/* / \____ _ _ ____ ______ / \ ____ __ _______ - * / / \/ \ / \/ \ / /\__\/ // \/ \ // /\__\ JΛVΛSLΛNG - * _/ / /\ \ \/ / /\ \\__\\ \ // /\ \ /\\/ \ /__\ \ Copyright 2014-2016 Javaslang, http://javaslang.io - * /___/\_/ \_/\____/\_/ \_/\__\/__/\__\_/ \_// \__/\_____/ Licensed under the Apache License, Version 2.0 - */ -package javaslang.collection; - -import javaslang.Tuple; -import javaslang.Tuple2; -import javaslang.control.Option; - -import java.util.Comparator; -import java.util.Objects; -import java.util.function.*; - -/** - * An abstract {@link Map} implementation (not intended to be public). - * - * @param Key type - * @param Value type - * @param Map type - * @author Ruslan Sennov - * @since 2.0.0 - */ -abstract class AbstractMap> implements Map { - - private static final long serialVersionUID = 1L; - - abstract M createFromEntries(Iterable> entries); - - /** - * Returns an empty version of this traversable, i.e. {@code empty().isEmpty() == true}. - * - * @return an empty instance of this Map. - */ - abstract M emptyInstance(); - - @SuppressWarnings("unchecked") - @Override - public M put(Tuple2 entry) { - Objects.requireNonNull(entry, "entry is null"); - return (M) put(entry._1, entry._2); - } - - @SuppressWarnings("unchecked") - @Override - public M put(K key, U value, - BiFunction merge) { - Objects.requireNonNull(merge, "the merge function is null"); - final Option currentValue = get(key); - if (currentValue.isEmpty()) { - return (M) put(key, value); - } else { - return (M) put(key, merge.apply(currentValue.get(), value)); - } - } - - @Override - public M put(Tuple2 entry, - BiFunction merge) { - Objects.requireNonNull(merge, "the merge function is null"); - final Option currentValue = get(entry._1); - if (currentValue.isEmpty()) { - return put(entry); - } else { - return put(entry.map2( - value -> merge.apply(currentValue.get(), value))); - } - } - - @SuppressWarnings("unchecked") - @Override - public M distinct() { - return (M) this; - } - - @Override - public M distinctBy(Comparator> comparator) { - Objects.requireNonNull(comparator, "comparator is null"); - return createFromEntries(iterator().distinctBy(comparator)); - } - - @Override - public M distinctBy(Function, ? extends U> keyExtractor) { - Objects.requireNonNull(keyExtractor, "keyExtractor is null"); - return createFromEntries(iterator().distinctBy(keyExtractor)); - } - - @Override - @SuppressWarnings("unchecked") - public M drop(int n) { - if (n <= 0) { - return (M) this; - } else if (n >= length()) { - return emptyInstance(); - } else { - return createFromEntries(iterator().drop(n)); - } - } - - @Override - @SuppressWarnings("unchecked") - public M dropRight(int n) { - if (n <= 0) { - return (M) this; - } else if (n >= length()) { - return emptyInstance(); - } else { - return createFromEntries(iterator().dropRight(n)); - } - } - - @Override - public M dropUntil(Predicate> predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return dropWhile(predicate.negate()); - } - - @Override - public M dropWhile(Predicate> predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return createFromEntries(iterator().dropWhile(predicate)); - } - - @Override - public M filter(Predicate> predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return createFromEntries(iterator().filter(predicate)); - } - - @Override - public M filter(BiPredicate predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return filter(t -> predicate.test(t._1, t._2)); - } - - @Override - public M filterKeys(Predicate predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return filter(t -> predicate.test(t._1)); - } - - @Override - public M filterValues(Predicate predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return filter(t -> predicate.test(t._2)); - } - - @Override - public M removeAll(BiPredicate predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return filter(predicate.negate()); - } - - @Override - public M removeKeys(Predicate predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return filterKeys(predicate.negate()); - } - - @Override - public M removeValues(Predicate predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return filterValues(predicate.negate()); - } - - @SuppressWarnings("unchecked") - @Override - public Map groupBy(Function, ? extends C> classifier) { - return Collections.groupBy(this, classifier, this::createFromEntries); - } - - @Override - public Iterator grouped(int size) { - return sliding(size, size); - } - - @Override - public abstract M init(); - - @Override - public Option initOption() { - return isEmpty() ? Option.none() : Option.some(init()); - } - - @Override - public abstract M tail(); - - @Override - public Option tailOption() { - return isEmpty() ? Option.none() : Option.some(tail()); - } - - @SuppressWarnings("unchecked") - @Override - public M take(int n) { - if (size() <= n) { - return (M) this; - } else { - return createFromEntries(iterator().take(n)); - } - } - - @SuppressWarnings("unchecked") - @Override - public M takeRight(int n) { - if (size() <= n) { - return (M) this; - } else { - return createFromEntries(iterator().takeRight(n)); - } - } - - @Override - public M takeUntil(Predicate> predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - return takeWhile(predicate.negate()); - } - - @SuppressWarnings("unchecked") - @Override - public M takeWhile(Predicate> predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - final M taken = createFromEntries(iterator().takeWhile(predicate)); - return taken.length() == length() ? (M) this : taken; - } - - @SuppressWarnings("unchecked") - @Override - public M merge(Map that) { - Objects.requireNonNull(that, "that is null"); - if (isEmpty()) { - return createFromEntries(that); - } else if (that.isEmpty()) { - return (M) this; - } else { - return that.foldLeft((M) this, (map, entry) -> !map.containsKey(entry._1) ? map.put(entry) : map); - } - } - - @SuppressWarnings("unchecked") - @Override - public M merge(Map that, BiFunction collisionResolution) { - Objects.requireNonNull(that, "that is null"); - Objects.requireNonNull(collisionResolution, "collisionResolution is null"); - if (isEmpty()) { - return createFromEntries(that); - } else if (that.isEmpty()) { - return (M) this; - } else { - return that.foldLeft((M) this, (map, entry) -> { - final K key = entry._1; - final U value = entry._2; - final V newValue = map.get(key).map(v -> (V) collisionResolution.apply(v, value)).getOrElse(value); - return (M) map.put(key, newValue); - }); - } - } - - @Override - public Tuple2 partition(Predicate> predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - final Tuple2>, Iterator>> p = iterator().partition(predicate); - return Tuple.of(createFromEntries(p._1), createFromEntries(p._2)); - } - - @SuppressWarnings("unchecked") - @Override - public M peek(Consumer> action) { - Objects.requireNonNull(action, "action is null"); - if (!isEmpty()) { - action.accept(head()); - } - return (M) this; - } - - @SuppressWarnings("unchecked") - @Override - public M replace(Tuple2 currentElement, Tuple2 newElement) { - Objects.requireNonNull(currentElement, "currentElement is null"); - Objects.requireNonNull(newElement, "newElement is null"); - return (M) (containsKey(currentElement._1) ? remove(currentElement._1).put(newElement) : this); - } - - @Override - public M replaceAll(Tuple2 currentElement, Tuple2 newElement) { - return replace(currentElement, newElement); - } - - @Override - public M scan(Tuple2 zero, BiFunction, ? super Tuple2, ? extends Tuple2> operation) { - Objects.requireNonNull(operation, "operation is null"); - return Collections.scanLeft(this, zero, operation, emptyInstance(), (m, e) -> m.put(e), Function.identity()); - } - - @Override - public Iterator sliding(int size) { - return sliding(size, 1); - } - - @Override - public Iterator sliding(int size, int step) { - return iterator().sliding(size, step).map(this::createFromEntries); - } - - @Override - public Tuple2 span(Predicate> predicate) { - Objects.requireNonNull(predicate, "predicate is null"); - final Tuple2>, Iterator>> t = iterator().span(predicate); - return Tuple.of(createFromEntries(t._1), createFromEntries(t._2)); - } -} diff --git a/javaslang/src/main/java/javaslang/collection/HashMap.java b/javaslang/src/main/java/javaslang/collection/HashMap.java index a53dc67b3e..5eddbeb3bd 100644 --- a/javaslang/src/main/java/javaslang/collection/HashMap.java +++ b/javaslang/src/main/java/javaslang/collection/HashMap.java @@ -5,11 +5,16 @@ */ package javaslang.collection; -import javaslang.*; +import javaslang.Kind2; +import javaslang.Tuple; +import javaslang.Tuple2; import javaslang.control.Option; import java.io.Serializable; -import java.util.*; +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; @@ -20,7 +25,7 @@ * @author Ruslan Sennov, Patryk Najda, Daniel Dietrich * @since 2.0.0 */ -public final class HashMap extends AbstractMap> implements Kind2, K, V>, Serializable { +public final class HashMap implements Kind2, K, V>, Map, Serializable { private static final long serialVersionUID = 1L; @@ -225,16 +230,6 @@ public static HashMap ofEntries(Iterable createFromEntries(Iterable> entries) { - return HashMap.ofEntries(entries); - } - - @Override - HashMap emptyInstance() { - return HashMap.empty(); - } - @Override public HashMap bimap(Function keyMapper, Function valueMapper) { Objects.requireNonNull(keyMapper, "keyMapper is null"); @@ -248,6 +243,61 @@ public boolean containsKey(K key) { return trie.containsKey(key); } + @Override + public HashMap distinct() { + return Maps.distinct(this); + } + + @Override + public HashMap distinctBy(Comparator> comparator) { + return Maps.distinctBy(this, this::createFromEntries, comparator); + } + + @Override + public HashMap distinctBy(Function, ? extends U> keyExtractor) { + return Maps.distinctBy(this, this::createFromEntries, keyExtractor); + } + + @Override + public HashMap drop(int n) { + return Maps.drop(this, this::createFromEntries, HashMap::empty, n); + } + + @Override + public HashMap dropRight(int n) { + return Maps.dropRight(this, this::createFromEntries, HashMap::empty, n); + } + + @Override + public HashMap dropUntil(Predicate> predicate) { + return Maps.dropUntil(this, this::createFromEntries, predicate); + } + + @Override + public HashMap dropWhile(Predicate> predicate) { + return Maps.dropWhile(this, this::createFromEntries, predicate); + } + + @Override + public HashMap filter(BiPredicate predicate) { + return Maps.filter(this, this::createFromEntries, predicate); + } + + @Override + public HashMap filter(Predicate> predicate) { + return Maps.filter(this, this::createFromEntries, predicate); + } + + @Override + public HashMap filterKeys(Predicate predicate) { + return Maps.filterKeys(this, this::createFromEntries, predicate); + } + + @Override + public HashMap filterValues(Predicate predicate) { + return Maps.filterValues(this, this::createFromEntries, predicate); + } + @Override public HashMap flatMap(BiFunction>> mapper) { Objects.requireNonNull(mapper, "mapper is null"); @@ -264,6 +314,16 @@ public Option get(K key) { return trie.get(key); } + @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() { if (isEmpty()) { @@ -282,6 +342,11 @@ public HashMap init() { } } + @Override + public Option> initOption() { + return Maps.initOption(this); + } + @Override public boolean isEmpty() { return trie.isEmpty(); @@ -320,17 +385,59 @@ public HashMap mapValues(Function valueMapp return map((k, v) -> Tuple.of(k, valueMapper.apply(v))); } + @Override + public HashMap merge(Map that) { + return Maps.merge(this, this::createFromEntries, that); + } + + @Override + public HashMap merge(Map that, + BiFunction collisionResolution) { + return Maps.merge(this, this::createFromEntries, that, collisionResolution); + } + + @Override + public Tuple2, HashMap> partition(Predicate> predicate) { + return Maps.partition(this, this::createFromEntries, predicate); + } + + @Override + public HashMap peek(Consumer> action) { + return Maps.peek(this, action); + } + + @Override + public HashMap put(K key, U value, BiFunction merge) { + return Maps.put(this, key, value, merge); + } + @Override public HashMap put(K key, V value) { return new HashMap<>(trie.put(key, value)); } + @Override + public HashMap put(Tuple2 entry) { + return Maps.put(this, entry); + } + + @Override + public HashMap put(Tuple2 entry, + BiFunction merge) { + return Maps.put(this, entry, merge); + } + @Override public HashMap remove(K key) { final HashArrayMappedTrie result = trie.remove(key); return result.size() == trie.size() ? this : wrap(result); } + @Override + public HashMap removeAll(BiPredicate predicate) { + return Maps.removeAll(this, this::createFromEntries, predicate); + } + @Override public HashMap removeAll(Iterable keys) { Objects.requireNonNull(keys, "keys is null"); @@ -348,6 +455,26 @@ public HashMap removeAll(Iterable keys) { } } + @Override + public HashMap removeKeys(Predicate predicate) { + return Maps.removeKeys(this, this::createFromEntries, predicate); + } + + @Override + public HashMap removeValues(Predicate predicate) { + return Maps.removeValues(this, this::createFromEntries, predicate); + } + + @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> elements) { Objects.requireNonNull(elements, "elements is null"); @@ -360,11 +487,33 @@ public HashMap retainAll(Iterable> elements) { return wrap(tree); } + @Override + public HashMap scan( + Tuple2 zero, + BiFunction, ? super Tuple2, ? extends Tuple2> operation) { + return Maps.scan(this, HashMap::empty, zero, operation); + } + @Override public int size() { return trie.size(); } + @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, HashMap> span(Predicate> predicate) { + return Maps.span(this, this::createFromEntries, predicate); + } + @Override public HashMap tail() { if (trie.isEmpty()) { @@ -374,6 +523,31 @@ public HashMap tail() { } } + @Override + public Option> tailOption() { + return Maps.tailOption(this); + } + + @Override + public HashMap take(int n) { + return Maps.take(this, this::createFromEntries, n); + } + + @Override + public HashMap takeRight(int n) { + return Maps.takeRight(this, this::createFromEntries, n); + } + + @Override + public HashMap takeUntil(Predicate> predicate) { + return Maps.takeUntil(this, this::createFromEntries, predicate); + } + + @Override + public HashMap takeWhile(Predicate> predicate) { + return Maps.takeWhile(this, this::createFromEntries, predicate); + } + @Override public java.util.HashMap toJavaMap() { return toJavaMap(java.util.HashMap::new, t -> t); @@ -418,4 +592,10 @@ public String toString() { 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); + } } diff --git a/javaslang/src/main/java/javaslang/collection/LinkedHashMap.java b/javaslang/src/main/java/javaslang/collection/LinkedHashMap.java index 8ffb67bc2b..7313c76ac2 100644 --- a/javaslang/src/main/java/javaslang/collection/LinkedHashMap.java +++ b/javaslang/src/main/java/javaslang/collection/LinkedHashMap.java @@ -5,7 +5,6 @@ */ package javaslang.collection; -import javaslang.Function2; import javaslang.Kind2; import javaslang.Tuple; import javaslang.Tuple2; @@ -13,6 +12,7 @@ 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; @@ -23,7 +23,7 @@ * @author Ruslan Sennov * @since 2.0.0 */ -public final class LinkedHashMap extends AbstractMap> implements Kind2, K, V>, Serializable { +public final class LinkedHashMap implements Kind2, K, V>, Map, Serializable { private static final long serialVersionUID = 1L; @@ -240,16 +240,6 @@ public static LinkedHashMap ofEntries(Iterable createFromEntries(Iterable> entries) { - return LinkedHashMap.ofEntries(entries); - } - - @Override - LinkedHashMap emptyInstance() { - return LinkedHashMap.empty(); - } - @Override public LinkedHashMap bimap(Function keyMapper, Function valueMapper) { Objects.requireNonNull(keyMapper, "keyMapper is null"); @@ -263,6 +253,61 @@ 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 filter(Predicate> predicate) { + return Maps.filter(this, this::createFromEntries, predicate); + } + + @Override + public LinkedHashMap filterKeys(Predicate predicate) { + return Maps.filterKeys(this, this::createFromEntries, predicate); + } + + @Override + public LinkedHashMap filterValues(Predicate predicate) { + return Maps.filterValues(this, this::createFromEntries, predicate); + } + @Override public LinkedHashMap flatMap(BiFunction>> mapper) { Objects.requireNonNull(mapper, "mapper is null"); @@ -279,6 +324,16 @@ public Option get(K key) { return map.get(key); } + @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(); @@ -293,6 +348,11 @@ public LinkedHashMap init() { } } + @Override + public Option> initOption() { + return Maps.initOption(this); + } + @Override public boolean isEmpty() { return map.isEmpty(); @@ -331,6 +391,32 @@ public LinkedHashMap mapValues(Function mapper 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 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); + } + @Override public LinkedHashMap put(K key, V value) { Queue> newList = list; @@ -344,6 +430,17 @@ public LinkedHashMap put(K key, V value) { return new LinkedHashMap<>(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)) { @@ -355,6 +452,11 @@ public LinkedHashMap remove(K key) { } } + @Override + public LinkedHashMap removeAll(BiPredicate predicate) { + return Maps.removeAll(this, this::createFromEntries, predicate); + } + @Override public LinkedHashMap removeAll(Iterable keys) { Objects.requireNonNull(keys, "keys is null"); @@ -364,6 +466,26 @@ public LinkedHashMap removeAll(Iterable keys) { return newList.size() == size() ? this : wrap(newList, newMap); } + @Override + public LinkedHashMap removeKeys(Predicate predicate) { + return Maps.removeKeys(this, this::createFromEntries, predicate); + } + + @Override + public LinkedHashMap removeValues(Predicate predicate) { + return Maps.removeValues(this, this::createFromEntries, predicate); + } + + @Override + public LinkedHashMap replace(Tuple2 currentElement, Tuple2 newElement) { + return Maps.replace(this, currentElement, newElement); + } + + @Override + public LinkedHashMap replaceAll(Tuple2 currentElement, Tuple2 newElement) { + return Maps.replaceAll(this, currentElement, newElement); + } + @Override public LinkedHashMap retainAll(Iterable> elements) { Objects.requireNonNull(elements, "elements is null"); @@ -376,11 +498,33 @@ public LinkedHashMap retainAll(Iterable> elements) return result; } + @Override + public LinkedHashMap scan( + Tuple2 zero, + BiFunction, ? super Tuple2, ? extends Tuple2> operation) { + return Maps.scan(this, LinkedHashMap::empty, zero, operation); + } + @Override public int size() { return map.size(); } + @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()) { @@ -390,6 +534,31 @@ public LinkedHashMap 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); @@ -434,4 +603,11 @@ public String toString() { 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); + } + } diff --git a/javaslang/src/main/java/javaslang/collection/Maps.java b/javaslang/src/main/java/javaslang/collection/Maps.java new file mode 100644 index 0000000000..2005ae4af3 --- /dev/null +++ b/javaslang/src/main/java/javaslang/collection/Maps.java @@ -0,0 +1,275 @@ +/* / \____ _ _ ____ ______ / \ ____ __ _______ + * / / \/ \ / \/ \ / /\__\/ // \/ \ // /\__\ JΛVΛSLΛNG + * _/ / /\ \ \/ / /\ \\__\\ \ // /\ \ /\\/ \ /__\ \ Copyright 2014-2016 Javaslang, http://javaslang.io + * /___/\_/ \_/\____/\_/ \_/\__\/__/\__\_/ \_// \__/\_____/ Licensed under the Apache License, Version 2.0 + */ +package javaslang.collection; + +import javaslang.Tuple; +import javaslang.Tuple2; +import javaslang.control.Option; + +import java.util.Comparator; +import java.util.Objects; +import java.util.function.*; + +/** + * Implementations of common {@code Map} functions (not intended to be public). + * + * @author Ruslan Sennov, Daniel Dietrich + * @since 2.0.0 + */ +final class Maps { + + static > M distinct(M map) { + return map; + } + + static > M distinctBy(M map, OfEntries ofEntries, + Comparator> comparator) { + Objects.requireNonNull(comparator, "comparator is null"); + return ofEntries.apply(map.iterator().distinctBy(comparator)); + } + + static > M distinctBy( + M map, OfEntries ofEntries, Function, ? extends U> keyExtractor) { + Objects.requireNonNull(keyExtractor, "keyExtractor is null"); + return ofEntries.apply(map.iterator().distinctBy(keyExtractor)); + } + + static > M drop(M map, OfEntries ofEntries, Supplier emptySupplier, int n) { + if (n <= 0) { + return map; + } else if (n >= map.size()) { + return emptySupplier.get(); + } else { + return ofEntries.apply(map.iterator().drop(n)); + } + } + + static > M dropRight(M map, OfEntries ofEntries, Supplier emptySupplier, + int n) { + if (n <= 0) { + return map; + } else if (n >= map.size()) { + return emptySupplier.get(); + } else { + return ofEntries.apply(map.iterator().dropRight(n)); + } + } + + static > M dropUntil(M map, OfEntries ofEntries, + Predicate> predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return dropWhile(map, ofEntries, predicate.negate()); + } + + static > M dropWhile(M map, OfEntries ofEntries, + Predicate> predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return ofEntries.apply(map.iterator().dropWhile(predicate)); + } + + static > M filter(M map, OfEntries ofEntries, + BiPredicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return filter(map, ofEntries, t -> predicate.test(t._1, t._2)); + } + + static > M filter(M map, OfEntries ofEntries, + Predicate> predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return ofEntries.apply(map.iterator().filter(predicate)); + } + + static > M filterKeys(M map, OfEntries ofEntries, + Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return filter(map, ofEntries, t -> predicate.test(t._1)); + } + + static > M filterValues(M map, OfEntries ofEntries, + Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return filter(map, ofEntries, t -> predicate.test(t._2)); + } + + static > Map groupBy(M map, OfEntries ofEntries, + Function, ? extends C> classifier) { + return Collections.groupBy(map, classifier, ofEntries); + } + + static > Iterator grouped(M map, OfEntries ofEntries, int size) { + return sliding(map, ofEntries, size, size); + } + + @SuppressWarnings("unchecked") + static > Option initOption(M map) { + return map.isEmpty() ? Option.none() : Option.some((M) map.init()); + } + + static > M merge(M map, OfEntries ofEntries, + Map that) { + Objects.requireNonNull(that, "that is null"); + if (map.isEmpty()) { + return ofEntries.apply(Map.narrow(that)); + } else if (that.isEmpty()) { + return map; + } else { + return that.foldLeft(map, (result, entry) -> !result.containsKey(entry._1) ? put(result, entry) : result); + } + } + + @SuppressWarnings("unchecked") + static > M merge( + M map, OfEntries ofEntries, + Map that, BiFunction collisionResolution) { + Objects.requireNonNull(that, "that is null"); + Objects.requireNonNull(collisionResolution, "collisionResolution is null"); + if (map.isEmpty()) { + return ofEntries.apply(Map.narrow(that)); + } else if (that.isEmpty()) { + return map; + } else { + return that.foldLeft(map, (result, entry) -> { + final K key = entry._1; + final U value = entry._2; + final V newValue = result.get(key).map(v -> (V) collisionResolution.apply(v, value)).getOrElse(value); + return (M) result.put(key, newValue); + }); + } + } + + static > Tuple2 partition(M map, OfEntries ofEntries, + Predicate> predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + final Tuple2>, Iterator>> p = map.iterator().partition(predicate); + return Tuple.of(ofEntries.apply(p._1), ofEntries.apply(p._2)); + } + + static > M peek(M map, Consumer> action) { + Objects.requireNonNull(action, "action is null"); + if (!map.isEmpty()) { + action.accept(map.head()); + } + return map; + } + + @SuppressWarnings("unchecked") + static > M put(M map, K key, U value, + BiFunction merge) { + Objects.requireNonNull(merge, "the merge function is null"); + final Option currentValue = map.get(key); + if (currentValue.isEmpty()) { + return (M) map.put(key, value); + } else { + return (M) map.put(key, merge.apply(currentValue.get(), value)); + } + } + + @SuppressWarnings("unchecked") + static > M put(M map, Tuple2 entry) { + Objects.requireNonNull(entry, "entry is null"); + return (M) map.put(entry._1, entry._2); + } + + static > M put(M map, Tuple2 entry, + BiFunction merge) { + Objects.requireNonNull(merge, "the merge function is null"); + final Option currentValue = map.get(entry._1); + if (currentValue.isEmpty()) { + return put(map, entry); + } else { + return put(map, entry.map2(value -> merge.apply(currentValue.get(), value))); + } + } + + static > M removeAll(M map, OfEntries ofEntries, + BiPredicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return filter(map, ofEntries, predicate.negate()); + } + + static > M removeKeys(M map, OfEntries ofEntries, + Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return filterKeys(map, ofEntries, predicate.negate()); + } + + static > M removeValues(M map, OfEntries ofEntries, + Predicate predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return filterValues(map, ofEntries, predicate.negate()); + } + + @SuppressWarnings("unchecked") + static > M replace(M map, Tuple2 currentElement, Tuple2 newElement) { + Objects.requireNonNull(currentElement, "currentElement is null"); + Objects.requireNonNull(newElement, "newElement is null"); + return (M) (map.containsKey(currentElement._1) ? map.remove(currentElement._1).put(newElement) : map); + } + + static > M replaceAll(M map, Tuple2 currentElement, Tuple2 newElement) { + return replace(map, currentElement, newElement); + } + + static > M scan( + M map, Supplier emptySupplier, Tuple2 zero, + BiFunction, ? super Tuple2, ? extends Tuple2> operation) { + Objects.requireNonNull(operation, "operation is null"); + return Collections.scanLeft(map, zero, operation, emptySupplier.get(), Maps::put, Function.identity()); + } + + static > Iterator sliding(M map, OfEntries ofEntries, int size) { + return sliding(map, ofEntries, size, 1); + } + + static > Iterator sliding(M map, OfEntries ofEntries, int size, int step) { + return map.iterator().sliding(size, step).map(ofEntries); + } + + static > Tuple2 span(M map, OfEntries ofEntries, + Predicate> predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + final Tuple2>, Iterator>> t = map.iterator().span(predicate); + return Tuple.of(ofEntries.apply(t._1), ofEntries.apply(t._2)); + } + + @SuppressWarnings("unchecked") + static > Option tailOption(M map) { + return map.isEmpty() ? Option.none() : Option.some((M) map.tail()); + } + + static > M take(M map, OfEntries ofEntries, int n) { + if (n >= map.size()) { + return map; + } else { + return ofEntries.apply(map.iterator().take(n)); + } + } + + static > M takeRight(M map, OfEntries ofEntries, int n) { + if (n >= map.size()) { + return map; + } else { + return ofEntries.apply(map.iterator().takeRight(n)); + } + } + + static > M takeUntil(M map, OfEntries ofEntries, + Predicate> predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + return takeWhile(map, ofEntries, predicate.negate()); + } + + static > M takeWhile(M map, OfEntries ofEntries, + Predicate> predicate) { + Objects.requireNonNull(predicate, "predicate is null"); + final M taken = ofEntries.apply(map.iterator().takeWhile(predicate)); + return taken.size() == map.size() ? map : taken; + } + + @FunctionalInterface + interface OfEntries> extends Function>, M> { + } +} diff --git a/javaslang/src/main/java/javaslang/collection/TreeMap.java b/javaslang/src/main/java/javaslang/collection/TreeMap.java index 9e7c5360fe..228de5ec3c 100644 --- a/javaslang/src/main/java/javaslang/collection/TreeMap.java +++ b/javaslang/src/main/java/javaslang/collection/TreeMap.java @@ -29,7 +29,7 @@ * @since 2.0.0 */ // DEV-NOTE: use entries.min().get() in favor of iterator().next(), it is faster! -public final class TreeMap extends AbstractMap> implements Kind2, K, V>, SortedMap, Serializable { +public final class TreeMap implements Kind2, K, V>, SortedMap, Serializable { private static final long serialVersionUID = 1L; @@ -392,16 +392,6 @@ public static TreeMap ofEntries(Comparator keyComparator } } - @Override - TreeMap createFromEntries(Iterable> it) { - return createTreeMap(entries.comparator(), it); - } - - @Override - TreeMap emptyInstance() { - return isEmpty() ? this : new TreeMap<>(entries.emptyInstance()); - } - @Override public TreeMap bimap(Function keyMapper, Function valueMapper) { return bimap(naturalComparator(), keyMapper, valueMapper); @@ -413,7 +403,7 @@ public TreeMap bimap(Comparator keyComparator, Objects.requireNonNull(keyMapper, "keyMapper is null"); Objects.requireNonNull(valueMapper, "valueMapper is null"); return createTreeMap(new EntryComparator<>(keyComparator), - entries.iterator().map(entry -> Tuple.of(keyMapper.apply(entry._1), valueMapper.apply(entry._2)))); + entries.iterator().map(entry -> Tuple.of(keyMapper.apply(entry._1), valueMapper.apply(entry._2)))); } @Override @@ -422,6 +412,61 @@ public boolean containsKey(K key) { return entries.contains(new Tuple2<>(key, ignored)); } + @Override + public TreeMap distinct() { + return Maps.distinct(this); + } + + @Override + public TreeMap distinctBy(Comparator> comparator) { + return Maps.distinctBy(this, this::createFromEntries, comparator); + } + + @Override + public TreeMap distinctBy(Function, ? extends U> keyExtractor) { + return Maps.distinctBy(this, this::createFromEntries, keyExtractor); + } + + @Override + public TreeMap drop(int n) { + return Maps.drop(this, this::createFromEntries, this::emptyInstance, n); + } + + @Override + public TreeMap dropRight(int n) { + return Maps.dropRight(this, this::createFromEntries, this::emptyInstance, n); + } + + @Override + public TreeMap dropUntil(Predicate> predicate) { + return Maps.dropUntil(this, this::createFromEntries, predicate); + } + + @Override + public TreeMap dropWhile(Predicate> predicate) { + return Maps.dropWhile(this, this::createFromEntries, predicate); + } + + @Override + public TreeMap filter(BiPredicate predicate) { + return Maps.filter(this, this::createFromEntries, predicate); + } + + @Override + public TreeMap filter(Predicate> predicate) { + return Maps.filter(this, this::createFromEntries, predicate); + } + + @Override + public TreeMap filterKeys(Predicate predicate) { + return Maps.filterKeys(this, this::createFromEntries, predicate); + } + + @Override + public TreeMap filterValues(Predicate predicate) { + return Maps.filterValues(this, this::createFromEntries, predicate); + } + @Override public TreeMap flatMap(BiFunction>> mapper) { return flatMap(naturalComparator(), mapper); @@ -432,7 +477,7 @@ public TreeMap flatMap(Comparator keyComparator, BiFunction>> mapper) { Objects.requireNonNull(mapper, "mapper is null"); return createTreeMap(new EntryComparator<>(keyComparator), - entries.iterator().flatMap(entry -> mapper.apply(entry._1, entry._2))); + entries.iterator().flatMap(entry -> mapper.apply(entry._1, entry._2))); } @Override @@ -441,6 +486,16 @@ public Option get(K key) { return entries.find(new Tuple2<>(key, ignored)).map(Tuple2::_2); } + @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() { if (isEmpty()) { @@ -460,6 +515,11 @@ public TreeMap init() { } } + @Override + public Option> initOption() { + return Maps.initOption(this); + } + @Override public boolean isEmpty() { return entries.isEmpty(); @@ -491,7 +551,7 @@ public TreeMap map(Comparator keyComparator, BiFunction> mapper) { Objects.requireNonNull(mapper, "mapper is null"); return createTreeMap(new EntryComparator<>(keyComparator), - entries.iterator().map(entry -> mapper.apply(entry._1, entry._2))); + entries.iterator().map(entry -> mapper.apply(entry._1, entry._2))); } @Override @@ -512,11 +572,48 @@ public TreeMap mapValues(Function valueMapper) return map(keyComparator(), (k, v) -> Tuple.of(k, valueMapper.apply(v))); } + @Override + public TreeMap merge(Map that) { + return Maps.merge(this, this::createFromEntries, that); + } + + @Override + public TreeMap merge(Map that, + BiFunction collisionResolution) { + return Maps.merge(this, this::createFromEntries, that, collisionResolution); + } + + @Override + public Tuple2, TreeMap> partition(Predicate> predicate) { + return Maps.partition(this, this::createFromEntries, predicate); + } + + @Override + public TreeMap peek(Consumer> action) { + return Maps.peek(this, action); + } + + @Override + public TreeMap put(K key, U value, BiFunction merge) { + return Maps.put(this, key, value, merge); + } + @Override public TreeMap put(K key, V value) { return new TreeMap<>(entries.insert(new Tuple2<>(key, value))); } + @Override + public TreeMap put(Tuple2 entry) { + return Maps.put(this, entry); + } + + @Override + public TreeMap put(Tuple2 entry, + BiFunction merge) { + return Maps.put(this, entry, merge); + } + @Override public TreeMap remove(K key) { final V ignored = null; @@ -528,6 +625,11 @@ public TreeMap remove(K key) { } } + @Override + public TreeMap removeAll(BiPredicate predicate) { + return Maps.removeAll(this, this::createFromEntries, predicate); + } + @Override public TreeMap removeAll(Iterable keys) { final V ignored = null; @@ -545,6 +647,26 @@ public TreeMap removeAll(Iterable keys) { } } + @Override + public TreeMap removeKeys(Predicate predicate) { + return Maps.removeKeys(this, this::createFromEntries, predicate); + } + + @Override + public TreeMap removeValues(Predicate predicate) { + return Maps.removeValues(this, this::createFromEntries, predicate); + } + + @Override + public TreeMap replace(Tuple2 currentElement, Tuple2 newElement) { + return Maps.replace(this, currentElement, newElement); + } + + @Override + public TreeMap replaceAll(Tuple2 currentElement, Tuple2 newElement) { + return Maps.replaceAll(this, currentElement, newElement); + } + @Override public TreeMap retainAll(Iterable> elements) { Objects.requireNonNull(elements, "elements is null"); @@ -557,11 +679,33 @@ public TreeMap retainAll(Iterable> elements) { return new TreeMap<>(tree); } + @Override + public TreeMap scan( + Tuple2 zero, + BiFunction, ? super Tuple2, ? extends Tuple2> operation) { + return Maps.scan(this, this::emptyInstance, zero, operation); + } + @Override public int size() { return entries.size(); } + @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, TreeMap> span(Predicate> predicate) { + return Maps.span(this, this::createFromEntries, predicate); + } + @Override public TreeMap tail() { if (isEmpty()) { @@ -572,6 +716,31 @@ public TreeMap tail() { } } + @Override + public Option> tailOption() { + return Maps.tailOption(this); + } + + @Override + public TreeMap take(int n) { + return Maps.take(this, this::createFromEntries, n); + } + + @Override + public TreeMap takeRight(int n) { + return Maps.takeRight(this, this::createFromEntries, n); + } + + @Override + public TreeMap takeUntil(Predicate> predicate) { + return Maps.takeUntil(this, this::createFromEntries, predicate); + } + + @Override + public TreeMap takeWhile(Predicate> predicate) { + return Maps.takeWhile(this, this::createFromEntries, predicate); + } + @Override public java.util.TreeMap toJavaMap() { return toJavaMap(() -> new java.util.TreeMap<>(keyComparator()), t -> t); @@ -598,8 +767,7 @@ private static TreeMap createTreeMap(Comparator entry : entries) { tree = tree.insert((Tuple2) entry); } - return tree.isEmpty() ? (TreeMap) empty() - : new TreeMap<>(tree); + return tree.isEmpty() ? (TreeMap) empty() : new TreeMap<>(tree); } // -- Object @@ -631,6 +799,14 @@ public String toString() { return mkString(stringPrefix() + "(", ", ", ")"); } + private TreeMap createFromEntries(Iterable> tuples) { + return createTreeMap(entries.comparator(), tuples); + } + + private TreeMap emptyInstance() { + return isEmpty() ? this : new TreeMap<>(entries.emptyInstance()); + } + /** * Used to compare entries by key and store the keyComparator for later access. * diff --git a/javaslang/src/test/java/outside_of_javaslang/IllegalAccessErrorTest.java b/javaslang/src/test/java/outside_of_javaslang/IllegalAccessErrorTest.java new file mode 100644 index 0000000000..26d4be9893 --- /dev/null +++ b/javaslang/src/test/java/outside_of_javaslang/IllegalAccessErrorTest.java @@ -0,0 +1,26 @@ +/* / \____ _ _ ____ ______ / \ ____ __ _______ + * / / \/ \ / \/ \ / /\__\/ // \/ \ // /\__\ JΛVΛSLΛNG + * _/ / /\ \ \/ / /\ \\__\\ \ // /\ \ /\\/ \ /__\ \ Copyright 2014-2016 Javaslang, http://javaslang.io + * /___/\_/ \_/\____/\_/ \_/\__\/__/\__\_/ \_// \__/\_____/ Licensed under the Apache License, Version 2.0 + */ +package outside_of_javaslang; + +import javaslang.collection.Array; +import javaslang.collection.HashMap; +import org.junit.Test; + +import java.util.function.BiFunction; + +import static org.assertj.core.api.Assertions.assertThat; + +public class IllegalAccessErrorTest { + + @Test + public void shouldNotThrowIllegalAccessErrorWhenUsingHashMapMergeMethodReference() { + final BiFunction, HashMap, HashMap> merge = HashMap::merge; + final HashMap reduced = Array.of("a", "b", "c") + .map(t -> HashMap.of(t, t)) + .reduce(merge); + assertThat(reduced).isEqualTo(HashMap.of("a", "a", "b", "b", "c", "c")); + } +}