
com.github.tonivade.purefun.data.ImmutableMap 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 java.io.Serial;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.pcollections.HashTreePMap;
import org.pcollections.PMap;
import com.github.tonivade.purefun.core.Consumer2;
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;
/**
* Similar to a HashMap
* @param the type of keys maintained by this map
* @param the type of mapped values
*/
public interface ImmutableMap extends Iterable> {
Map toMap();
ImmutableMap put(K key, V value);
ImmutableMap putAll(ImmutableMap extends K, ? extends V> other);
ImmutableMap remove(K key);
Option get(K key);
Sequence values();
ImmutableSet keys();
ImmutableSet> entries();
ImmutableMap merge(K key, V value, Operator2 merger);
int size();
@Override
default Iterator> iterator() {
return entries().iterator();
}
default void forEach(Consumer2 super K, ? super V> consumer) {
entries().forEach(tuple -> consumer.accept(tuple.get1(), tuple.get2()));
}
default ImmutableMap bimap(
Function1 super K, ? extends A> keyMapper,
Function1 super V, ? extends B> valueMapper) {
return ImmutableMap.from(entries().map(tuple -> tuple.map(keyMapper, valueMapper)));
}
default ImmutableMap mapKeys(Function1 super K, ? extends A> mapper) {
return ImmutableMap.from(entries().map(tuple -> tuple.map1(mapper)));
}
default ImmutableMap mapValues(Function1 super V, ? extends A> mapper) {
return ImmutableMap.from(entries().map(tuple -> tuple.map2(mapper)));
}
default ImmutableMap filterKeys(Matcher1 super K> filter) {
return ImmutableMap.from(entries().filter(tuple -> filter.match(tuple.get1())));
}
default ImmutableMap filterValues(Matcher1 super V> filter) {
return ImmutableMap.from(entries().filter(tuple -> filter.match(tuple.get2())));
}
default boolean containsKey(K key) {
return get(key).isPresent();
}
default ImmutableMap putIfAbsent(K key, V value) {
if (containsKey(key)) {
return this;
}
return put(key, value);
}
default V getOrDefault(K key, Producer extends V> supplier) {
return get(key).getOrElse(supplier);
}
default boolean isEmpty() {
return size() == 0;
}
@SafeVarargs
static ImmutableMap of(Tuple2... entries) {
return from(ImmutableSet.of(entries));
}
static Tuple2 entry(K key, V value) {
return Tuple2.of(key, value);
}
static ImmutableMap from(Map map) {
return from(map.entrySet());
}
@SuppressWarnings("unchecked")
static ImmutableMap empty() {
return (ImmutableMap) PImmutableMap.EMPTY;
}
static ImmutableMap from(Iterable> entries) {
return from(ImmutableSet.from(entries));
}
static ImmutableMap from(Stream> entries) {
return from(ImmutableSet.from(entries));
}
static ImmutableMap from(ImmutableSet> entries) {
LinkedHashMap collect = entries.stream().collect(toLinkedHashMap(Tuple2::get1, Tuple2::get2));
return new PImmutableMap<>(collect);
}
static ImmutableMap from(Set extends Map.Entry> entries) {
LinkedHashMap collect = entries.stream().collect(toLinkedHashMap(Map.Entry::getKey, Map.Entry::getValue));
return new PImmutableMap<>(collect);
}
static Collector> toImmutableMap(
Function1 super T, ? extends K> keyMapper, Function1 super T, ? extends V> valueMapper) {
Collector> toLinkedHashMap = toLinkedHashMap(keyMapper, valueMapper);
return collectingAndThen(toLinkedHashMap, PImmutableMap::new);
}
static Builder builder() {
return new Builder<>();
}
final class Builder {
private final Map map = new HashMap<>();
private Builder() {
}
public Builder put(K key, V value) {
map.put(key, value);
return this;
}
public ImmutableMap build() {
return ImmutableMap.from(map);
}
}
final class PImmutableMap implements ImmutableMap, Serializable {
@Serial
private static final long serialVersionUID = -7846127227891259826L;
private static final ImmutableMap, ?> EMPTY = new PImmutableMap<>(HashTreePMap.empty());
private static final Equal> EQUAL =
Equal.>of().comparing(a -> a.backend);
private final PMap backend;
private PImmutableMap(Map backend) {
this(HashTreePMap.from(backend));
}
private PImmutableMap(PMap backend) {
this.backend = checkNonNull(backend);
}
@Override
public Map toMap() {
return new HashMap<>(backend);
}
@Override
public ImmutableMap put(K key, V value) {
return new PImmutableMap<>(backend.plus(key, value));
}
@Override
public ImmutableMap putAll(ImmutableMap extends K, ? extends V> other) {
return new PImmutableMap<>(backend.plusAll(other.toMap()));
}
@Override
public ImmutableMap remove(K key) {
return new PImmutableMap<>(backend.minus(key));
}
@Override
public Option get(K key) {
return Option.of(backend.get(key));
}
@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 ImmutableMap 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 PImmutableMap<>(backend.minus(key));
}
return new PImmutableMap<>(backend.plus(key, newValue));
}
@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 "ImmutableMap(" + backend + ")";
}
@Serial
private Object readResolve() {
if (backend.isEmpty()) {
return EMPTY;
}
return this;
}
}
private static Collector> toLinkedHashMap(
Function1 super T, ? extends K> keyMapper,
Function1 super T, ? extends V> valueMapper) {
return Collectors.toMap(keyMapper::apply, valueMapper::apply, ImmutableMap::throwingMerge, LinkedHashMap::new);
}
private static V throwingMerge(V a, V b) {
throw new IllegalArgumentException("conflict detected");
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy