
com.github.tonivade.purefun.data.ImmutableTreeMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of purefun-core Show documentation
Show all versions of purefun-core Show documentation
Functional Programming Library for Java
The newest version!
/*
* Copyright (c) 2018-2024, Antonio Gabriel Muñoz Conejo
* Distributed under the terms of the MIT License
*/
package com.github.tonivade.purefun.data;
import static com.github.tonivade.purefun.core.Precondition.checkNonNull;
import static java.util.stream.Collectors.collectingAndThen;
import com.github.tonivade.purefun.core.Equal;
import com.github.tonivade.purefun.core.Function1;
import com.github.tonivade.purefun.core.Matcher1;
import com.github.tonivade.purefun.core.Operator2;
import com.github.tonivade.purefun.core.Producer;
import com.github.tonivade.purefun.core.Tuple;
import com.github.tonivade.purefun.core.Tuple2;
import com.github.tonivade.purefun.type.Option;
import com.github.tonivade.purefun.type.Try;
import java.io.Serial;
import java.io.Serializable;
import java.util.Comparator;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.SequencedMap;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.pcollections.PSortedMap;
import org.pcollections.TreePMap;
/**
* Similar to a TreeMap
* @param the type of keys maintained by this map
* @param the type of mapped values
*/
public interface ImmutableTreeMap extends ImmutableMap {
Comparator comparator();
NavigableMap toNavigableMap();
default SequencedMap toSequencedMap() {
return toNavigableMap();
}
default SortedMap toSortedMap() {
return toNavigableMap();
}
@Override
ImmutableTreeMap put(K key, V value);
@Override
ImmutableTreeMap putAll(ImmutableMap extends K, ? extends V> other);
@Override
ImmutableTreeMap remove(K key);
@Override
ImmutableTreeMap merge(K key, V value, Operator2 merger);
ImmutableTreeMap headMap(K toKey);
ImmutableTreeMap tailMap(K fromKey);
Option> headEntry();
Option> tailEntry();
Option> higherEntry(K key);
Option> lowerEntry(K key);
Option> floorEntry(K key);
Option> ceilingEntry(K key);
default Option headKey() {
return headEntry().map(Tuple2::get1);
}
default Option tailKey() {
return tailEntry().map(Tuple2::get1);
}
default Option higherKey(K key) {
return higherEntry(key).map(Tuple2::get1);
}
default Option lowerKey(K key) {
return lowerEntry(key).map(Tuple2::get1);
}
default Option floorKey(K key) {
return floorEntry(key).map(Tuple2::get1);
}
default Option ceilingKey(K key) {
return floorEntry(key).map(Tuple2::get1);
}
@Override
default ImmutableTreeMap bimap(Function1 super K, ? extends A> keyMapper, Function1 super V, ? extends B> valueMapper) {
return ImmutableTreeMap.from(entries().map(tuple -> tuple.map(keyMapper, valueMapper)));
}
default ImmutableTreeMap bimap(Comparator super A> comparator, Function1 super K, ? extends A> keyMapper, Function1 super V, ? extends B> valueMapper) {
return ImmutableTreeMap.from(comparator, entries().map(tuple -> tuple.map(keyMapper, valueMapper)));
}
@Override
default ImmutableTreeMap mapKeys(Function1 super K, ? extends A> mapper) {
return ImmutableTreeMap.from(entries().map(tuple -> tuple.map1(mapper)));
}
default ImmutableTreeMap mapKeys(Comparator super A> comparator, Function1 super K, ? extends A> mapper) {
return ImmutableTreeMap.from(comparator, entries().map(tuple -> tuple.map1(mapper)));
}
@Override
default ImmutableTreeMap mapValues(Function1 super V, ? extends A> mapper) {
return ImmutableTreeMap.from(entries().map(tuple -> tuple.map2(mapper)));
}
@Override
default ImmutableTreeMap filterKeys(Matcher1 super K> filter) {
return ImmutableTreeMap.from(entries().filter(tuple -> filter.match(tuple.get1())));
}
@Override
default ImmutableTreeMap filterValues(Matcher1 super V> filter) {
return ImmutableTreeMap.from(entries().filter(tuple -> filter.match(tuple.get2())));
}
@Override
default ImmutableTreeMap putIfAbsent(K key, V value) {
if (containsKey(key)) {
return this;
}
return put(key, value);
}
@Override
default V getOrDefault(K key, Producer extends V> supplier) {
return get(key).getOrElse(supplier);
}
@Override
default boolean isEmpty() {
return size() == 0;
}
@SafeVarargs
static ImmutableTreeMap of(Tuple2... entries) {
return from(naturalOrder(), ImmutableSet.of(entries));
}
static Tuple2 entry(K key, V value) {
return Tuple2.of(key, value);
}
static ImmutableTreeMap from(Comparator super K> comparator, Map map) {
return new PImmutableTreeMap<>(comparator, map);
}
static ImmutableTreeMap from(Map map) {
return new PImmutableTreeMap<>(naturalOrder(), map);
}
@SuppressWarnings("unchecked")
static ImmutableTreeMap empty() {
return (ImmutableTreeMap) PImmutableTreeMap.EMPTY;
}
static ImmutableTreeMap from(Stream> entries) {
return from(naturalOrder(), entries);
}
static ImmutableTreeMap from(Comparator super K> comparator, Stream> entries) {
return from(comparator, ImmutableSet.from(entries));
}
static ImmutableTreeMap from(ImmutableSet> entries) {
return from(naturalOrder(), entries);
}
static ImmutableTreeMap from(Comparator super K> comparator, ImmutableSet> entries) {
TreeMap treeMap = entries.stream()
.collect(toTreeMap(comparator, Tuple2::get1, Tuple2::get2));
return new PImmutableTreeMap<>(treeMap);
}
static ImmutableTreeMap from(Set> entries) {
return from(naturalOrder(), entries);
}
static ImmutableTreeMap from(Comparator super K> comparator, Set> entries) {
TreeMap treeMap = entries.stream()
.collect(toTreeMap(comparator, Map.Entry::getKey, Map.Entry::getValue));
return new PImmutableTreeMap<>(treeMap);
}
static Collector> toImmutableTreeMap(
Function1 super T, ? extends K> keyMapper, Function1 super T, ? extends V> valueMapper) {
return toImmutableTreeMap(naturalOrder(), keyMapper, valueMapper);
}
static Collector> toImmutableTreeMap(
Comparator super K> comparator, Function1 super T, ? extends K> keyMapper, Function1 super T, ? extends V> valueMapper) {
Collector> toTreeMap = toTreeMap(comparator, keyMapper, valueMapper);
return collectingAndThen(toTreeMap, PImmutableTreeMap::new);
}
static , V> Builder builder() {
return new Builder<>();
}
final class Builder, V> {
private final NavigableMap map = new TreeMap<>();
private Builder() { }
public Builder put(K key, V value) {
map.put(key, value);
return this;
}
public ImmutableTreeMap build() {
return ImmutableTreeMap.from(map);
}
}
@SuppressWarnings("unchecked")
private static Comparator naturalOrder() {
return (Comparator) Comparator.naturalOrder();
}
final class PImmutableTreeMap implements ImmutableTreeMap, Serializable {
@Serial
private static final long serialVersionUID = -3269335569221894587L;
private static final ImmutableTreeMap, ?> EMPTY = new PImmutableTreeMap<>(TreePMap.empty(naturalOrder()));
private static final Equal> EQUAL =
Equal.>of().comparing(a -> a.backend);
private final PSortedMap backend;
private PImmutableTreeMap(Comparator super K> comparator, Map extends K, ? extends V> backend) {
this(TreePMap.from(comparator, backend));
}
private PImmutableTreeMap(SortedMap backend) {
this(TreePMap.fromSortedMap(backend));
}
private PImmutableTreeMap(PSortedMap backend) {
this.backend = checkNonNull(backend);
}
@SuppressWarnings("unchecked")
@Override
public Comparator comparator() {
return (Comparator) backend.comparator();
}
@Override
public Map toMap() {
return toNavigableMap();
}
@Override
public NavigableMap toNavigableMap() {
return new TreeMap<>(backend);
}
@Override
public ImmutableTreeMap put(K key, V value) {
return new PImmutableTreeMap<>(backend.plus(key, value));
}
@Override
public ImmutableTreeMap putAll(ImmutableMap extends K, ? extends V> other) {
return new PImmutableTreeMap<>(backend.plusAll(other.toMap()));
}
@Override
public ImmutableTreeMap remove(K key) {
return new PImmutableTreeMap<>(backend.minus(key));
}
@Override
public Option get(K key) {
return Option.of(backend.get(key));
}
@Override
public ImmutableTreeMap merge(K key, V value, Operator2 merger) {
var oldValue = backend.get(key);
var newValue = oldValue == null ? value : merger.apply(oldValue, value);
if (newValue == null) {
return new PImmutableTreeMap<>(backend.minus(key));
}
return new PImmutableTreeMap<>(backend.plus(key, newValue));
}
@Override
public ImmutableTreeMap headMap(K toKey) {
return new PImmutableTreeMap<>(backend.headMap(toKey, false));
}
@Override
public ImmutableTreeMap tailMap(K fromKey) {
return new PImmutableTreeMap<>(backend.tailMap(fromKey, false));
}
@Override
public Option> headEntry() {
return Try.of(() -> Tuple.from(backend.firstEntry())).toOption();
}
@Override
public Option> tailEntry() {
return Try.of(() -> Tuple.from(backend.lastEntry())).toOption();
}
@Override
public Option> higherEntry(K key) {
return Try.of(() -> Tuple.from(backend.higherEntry(key))).toOption();
}
@Override
public Option> lowerEntry(K key) {
return Try.of(() -> Tuple.from(backend.lowerEntry(key))).toOption();
}
@Override
public Option> floorEntry(K key) {
return Try.of(() -> Tuple.from(backend.floorEntry(key))).toOption();
}
@Override
public Option> ceilingEntry(K key) {
return Try.of(() -> Tuple.from(backend.ceilingEntry(key))).toOption();
}
@Override
public Sequence values() {
return ImmutableList.from(backend.values());
}
@Override
public ImmutableSet keys() {
return ImmutableSet.from(backend.keySet());
}
@Override
public ImmutableSet> entries() {
return ImmutableSet.from(backend.entrySet()).map(Tuple::from);
}
@Override
public int size() {
return backend.size();
}
@Override
public int hashCode() {
return Objects.hash(backend);
}
@Override
public boolean equals(Object obj) {
return EQUAL.applyTo(this, obj);
}
@Override
public String toString() {
return "ImmutableTreeMap(" + backend + ")";
}
@Serial
private Object readResolve() {
// XXX: this is a issue in pcollections: see https://github.com/hrldcpr/pcollections/pull/115
if (backend.size() == 0) {
return EMPTY;
}
return this;
}
}
private static Collector> toTreeMap(
Comparator super K> comparator,
Function1 super T, ? extends K> keyMapper,
Function1 super T, ? extends V> valueMapper) {
return Collectors.toMap(keyMapper::apply, valueMapper::apply, ImmutableTreeMap::throwingMerge, () -> new TreeMap<>(comparator));
}
private static V throwingMerge(V a, V b) {
throw new IllegalArgumentException("conflict detected");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy