
fj.data.TreeMap Maven / Gradle / Ivy
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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy