fj.data.TreeMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of functionaljava Show documentation
Show all versions of functionaljava Show documentation
Functional Java is an open source library that supports closures for the Java programming language
package fj.data;
import fj.Equal;
import fj.F;
import fj.F1Functions;
import fj.Hash;
import fj.Ord;
import fj.P;
import fj.P2;
import fj.P3;
import fj.Show;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import static fj.Function.compose;
import static fj.Function.flip;
import static fj.P.p;
import static fj.data.IterableW.join;
import static fj.data.List.iterableList;
/**
* An immutable, in-memory map, backed by a red-black tree.
*/
public final class TreeMap implements Iterable> {
private final Set>> tree;
private TreeMap(final Set>> tree) {
this.tree = tree;
}
private static Ord> ord(final Ord keyOrd) {
return keyOrd.contramap(P2.__1());
}
/**
* Constructs an empty tree map.
*
* @param keyOrd An order for the keys of the tree map.
* @return an empty TreeMap with the given key order.
*/
public static TreeMap empty(final Ord keyOrd) {
return new TreeMap<>(Set.empty(TreeMap.ord(keyOrd)));
}
@Override
public boolean equals(Object other) {
return Equal.equals0(TreeMap.class, this, other, () -> Equal.treeMapEqual(Equal.anyEqual(), Equal.anyEqual()));
}
@Override
public int hashCode() {
return Hash.treeMapHash(Hash.anyHash(), Hash.anyHash()).hash(this);
}
@Override
public String toString() {
return Show.treeMapShow(Show.anyShow(), Show.anyShow()).showS(this);
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param p2s The elements to construct the tree map with.
* @return a TreeMap with the given elements.
*/
@SafeVarargs public static TreeMap treeMap(final Ord keyOrd, final P2... p2s) {
return arrayTreeMap(keyOrd, p2s);
}
/**
* Constructs a tree map from the given elements.
*
* @deprecated As of release 4.5, use {@link #iterableTreeMap(Ord, Iterable)}
*
* @param keyOrd An order for the keys of the tree map.
* @param list The elements to construct the tree map with.
* @return a TreeMap with the given elements.
*/
@Deprecated
public static TreeMap treeMap(final Ord keyOrd, final List> list) {
return iterableTreeMap(keyOrd, list);
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param it The elements to construct the tree map with.
* @return A TreeMap with the given elements.
*/
public static TreeMap iterableTreeMap(final Ord keyOrd, final Iterable> it) {
TreeMap tm = empty(keyOrd);
for (final P2 p2 : it) {
tm = tm.set(p2._1(), p2._2());
}
return tm;
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param it The elements to construct the tree map with.
* @return A TreeMap with the given elements.
*/
public static TreeMap iteratorTreeMap(final Ord keyOrd, final Iterator> it) {
return iterableTreeMap(keyOrd, () -> it);
}
/**
* Constructs a tree map from the given elements.
*
* @param keyOrd An order for the keys of the tree map.
* @param ps The elements to construct the tree map with.
* @return A TreeMap with the given elements.
*/
@SafeVarargs
public static TreeMap arrayTreeMap(final Ord keyOrd, final P2...ps) {
return iterableTreeMap(keyOrd, Array.array(ps));
}
/**
* Returns a potential value that the given key maps to.
*
* @param k The key to look up in the tree map.
* @return A potential value for the given key.
*/
public Option get(final K k) {
return tree.lookup(p(k, Option.none())).bind(P2::_2);
}
/**
* Inserts the given key and value association into the tree map.
* If the given key is already mapped to a value, the old value is replaced with the given one.
*
* @param k The key to insert.
* @param v The value to insert.
* @return A new tree map with the given value mapped to the given key.
*/
public TreeMap set(final K k, final V v) {
return new TreeMap<>(tree.insert(p(k, Option.some(v))));
}
/**
* Deletes the entry in the tree map that corresponds to the given key.
*
* @param k The key to delete from this tree map.
* @return A new tree map with the entry corresponding to the given key removed.
*/
public TreeMap delete(final K k) {
return new TreeMap<>(tree.delete(p(k, Option.none())));
}
/**
* Returns the number of entries in this tree map.
*
* @return The number of entries in this tree map.
*/
public int size() {
return tree.size();
}
/**
* Determines if this tree map has any entries.
*
* @return true
if this tree map has no entries, false
otherwise.
*/
public boolean isEmpty() {
return tree.isEmpty();
}
/**
* Returns all values in this tree map.
*
* @return All values in this tree map.
*/
public List values() {
return iterableList(join(tree.toList().map(compose(IterableW.wrap(), P2.__2()))));
}
/**
* Returns all keys in this tree map.
*
* @return All keys in this tree map.
*/
public List keys() {
return tree.toList().map(P2.__1());
}
/**
* Determines if the given key value exists in this tree map.
*
* @param k The key value to look for in this tree map.
* @return true
if this tree map contains the given key, false
otherwise.
*/
public boolean contains(final K k) {
return tree.member(p(k, Option.none()));
}
/**
* Returns an iterator for this map's key-value pairs.
* This method exists to permit the use in a for
-each loop.
*
* @return A iterator for this map's key-value pairs.
*/
public Iterator> iterator() {
return join(tree.toStream().map(P2.map2_(IterableW.wrap())
).map(P2.tuple(compose(IterableW.map(), P.p2())))).iterator();
}
/**
* A mutable map projection of this tree map.
*
* @return A new mutable map isomorphic to this tree map.
*/
public Map toMutableMap() {
final F>> fakePair = k -> p(k, Option.none());
final Comparator comparator = tree.ord().contramap(fakePair).toComparator();
final Map m = new java.util.TreeMap<>(comparator);
for (final P2 e : this) {
m.put(e._1(), e._2());
}
return m;
}
public Stream> toStream() {
return tree.toStream().map(p -> p.map2(o -> o.some()));
}
public Stream> toStreamReverse() {
return tree.toStreamReverse().map(p -> p.map2(o -> o.some()));
}
public List> toList() {
return tree.toList().map(p -> p.map2(o -> o.some()));
}
public List> toListReverse() {
return tree.toListReverse().map(p -> p.map2(o -> o.some()));
}
/**
* An immutable projection of the given mutable map.
*
* @param ord An order for the map's keys.
* @param m A mutable map to project to an immutable one.
* @return A new immutable tree map isomorphic to the given mutable map.
*/
public static TreeMap fromMutableMap(final Ord ord, final Map m) {
TreeMap t = empty(ord);
for (final Map.Entry e : m.entrySet()) {
t = t.set(e.getKey(), e.getValue());
}
return t;
}
/**
* Returns a first-class version of the get method for this TreeMap.
*
* @return a functional representation of this TreeMap.
*/
public F> get() {
return this::get;
}
/**
* Modifies the value for the given key, if present, by applying the given function to it.
*
* @param k The key for the value to modify.
* @param f A function with which to modify the value.
* @return A new tree map with the value for the given key transformed by the given function,
* paired with True if the map was modified, otherwise False.
*/
public P2> update(final K k, final F f) {
final P2>>> up =
tree.update(p(k, Option.none()), compose(P2.tuple(P.p2()), P2.map2_(Option.map().f(f))));
return p(up._1(), new TreeMap<>(up._2()));
}
/**
* Modifies the value for the given key, if present, by applying the given function to it, or
* inserts the given value if the key is not present.
*
* @param k The key for the value to modify.
* @param f A function with which to modify the value.
* @param v A value to associate with the given key if the key is not already present.
* @return A new tree map with the value for the given key transformed by the given function.
*/
public TreeMap update(final K k, final F f, final V v) {
final P2> up = update(k, f);
return up._1() ? up._2() : set(k, v);
}
/**
* Splits this TreeMap at the given key. Returns a triple of:
*
* - A set containing all the values of this map associated with keys less than the given key.
* - An option of a value mapped to the given key, if it exists in this map, otherwise None.
*
- A set containing all the values of this map associated with keys greater than the given key.
*
*
* @param k A key at which to split this map.
* @return Two sets and an optional value, where all elements in the first set are mapped to keys less than the given
* key in this map, all the elements in the second set are mapped to keys greater than the given key,
* and the optional value is the value associated with the given key if present, otherwise None.
*/
public P3, Option, Set> split(Ord ord, final K k) {
final F>>, Set> getSome = F1Functions.mapSet(F1Functions.o(Option.fromSome(), P2.__2()), ord);
return tree.split(p(k, Option.none())).map1(getSome).map3(getSome)
.map2(F1Functions.o(Option.join(), F1Functions.mapOption(P2.__2())));
}
/**
* Internal construction of a TreeMap from the given set.
* @param ord An order for the keys of the tree map.
* @param s The elements to construct the tree map with.
* @return a TreeMap with the given elements.
*/
private static TreeMap treeMap(Ord ord, Set>> s) {
TreeMap empty = TreeMap.empty(ord);
TreeMap tree = s.toList().foldLeft((tm, p2) -> {
Option opt = p2._2();
if (opt.isSome()) {
return tm.set(p2._1(), opt.some());
}
return tm;
}, empty);
return tree;
}
/**
* Splits this TreeMap at the given key. Returns a triple of:
*
* - A tree map containing all the values of this map associated with keys less than the given key.
* - An option of a value mapped to the given key, if it exists in this map, otherwise None.
*
- A tree map containing all the values of this map associated with keys greater than the given key.
*
*
* @param k A key at which to split this map.
* @return Two tree maps and an optional value, where all keys in the first tree map are mapped
* to keys less than the given key in this map, all the keys in the second tree map are mapped
* to keys greater than the given key, and the optional value is the value associated with the
* given key if present, otherwise None.
*/
public P3, Option, TreeMap> splitLookup(final K k) {
P3>>, Option>>, Set>>> p3 = tree.split(p(k, get(k)));
Ord o = tree.ord().contramap(k2 -> p(k2, Option.none()));
return p(treeMap(o, p3._1()), get(k), treeMap(o, p3._3()));
}
/**
* Maps the given function across the values of this TreeMap.
*
* @param f A function to apply to the values of this TreeMap.
* @return A new TreeMap with the values transformed by the given function.
*/
@SuppressWarnings("unchecked")
public TreeMap map(final F f) {
final F>, P2>> g = compose(p2 -> p(p2._1(), p2._2()), P2.map2_(F1Functions.mapOption(f)));
final F>> coord = flip(P.>p2()).f(Option.none());
final Ord o = tree.ord().contramap(coord);
return new TreeMap<>(tree.map(TreeMap.ord(o), g));
}
/**
* Returns the minimum (key, value) pair in the tree if the tree is not empty.
*/
public Option> min() {
return tree.min().map(p -> p(p._1(), p._2().some()));
}
/**
* Returns the minimum key in the tree if the tree is not empty.
*/
public Option minKey() {
return tree.min().map(P2::_1);
}
/**
* Returns the maximum (key, value) pair in the tree if the tree is not empty.
*/
public Option> max() {
return tree.max().map(p -> p(p._1(), p._2().some()));
}
/**
* Returns the maximum key in the tree if the tree is not empty.
*/
public Option maxKey() {
return tree.max().map(P2::_1);
}
/**
* The expression t1.union(t2)
takes the left-biased union of t1
* and t2
. It prefers t1
when duplicate keys are encountered.
*
* @param t2 The other tree we wish to combine with this one
* @return The combined TreeMap
*/
public TreeMap union(TreeMap t2) {
// TODO This could be implemented more efficiently using "hedge union"
TreeMap result = t2;
for(P2 p : this) {
result = result.set(p._1(), p._2());
}
return result;
}
/**
* The expression t1.union(t2)
takes the left-biased union of t1
* and t2
. It prefers t1
when duplicate keys are encountered.
*
* @param t2 The other list/set of pairs we wish to combine with this one
* @return The combined TreeMap
*/
public TreeMap union(Iterable> t2) {
TreeMap result = this;
for(P2 p : t2) {
if(!this.contains(p._1())) {
result = result.set(p._1(), p._2());
}
}
return result;
}
}