com.github.tonivade.purefun.data.ImmutableMap Maven / Gradle / Ivy
/*
* Copyright (c) 2018-2021, Antonio Gabriel Muñoz Conejo
* Distributed under the terms of the MIT License
*/
package com.github.tonivade.purefun.data;
import static java.util.Collections.unmodifiableMap;
import static java.util.stream.Collectors.collectingAndThen;
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.TreeMap;
import java.util.function.BinaryOperator;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.github.tonivade.purefun.Consumer2;
import com.github.tonivade.purefun.Equal;
import com.github.tonivade.purefun.Function1;
import com.github.tonivade.purefun.Matcher1;
import com.github.tonivade.purefun.Operator2;
import com.github.tonivade.purefun.Producer;
import com.github.tonivade.purefun.Tuple;
import com.github.tonivade.purefun.Tuple2;
import com.github.tonivade.purefun.type.Option;
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 map(
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) JavaBasedImmutableMap.EMPTY;
}
static ImmutableMap from(Iterable extends Tuple2> entries) {
return from(ImmutableSet.from(entries));
}
static ImmutableMap from(Stream extends Tuple2> entries) {
return from(ImmutableSet.from(entries));
}
static ImmutableMap from(ImmutableSet extends Tuple2> entries) {
return new JavaBasedImmutableMap<>(entries.stream()
.collect(ImmutableTreeModule.toLinkedHashMap(Tuple2::get1, Tuple2::get2)));
}
static ImmutableMap from(Set extends Map.Entry> entries) {
return new JavaBasedImmutableMap<>(entries.stream()
.collect(ImmutableTreeModule.toLinkedHashMap(Map.Entry::getKey, Map.Entry::getValue)));
}
static Collector> toImmutableMap(
Function1 super T, ? extends K> keyMapper, Function1 super T, ? extends V> valueMapper) {
Collector> toLinkedHashMap =
ImmutableTreeModule.toLinkedHashMap(keyMapper, valueMapper);
return collectingAndThen(toLinkedHashMap, JavaBasedImmutableMap::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 JavaBasedImmutableMap implements ImmutableMap, Serializable {
private static final long serialVersionUID = -1236334562860351635L;
private static final ImmutableMap, ?> EMPTY = new JavaBasedImmutableMap<>(new LinkedHashMap<>());
private static final Equal> EQUAL =
Equal.>of().comparing(a -> a.backend);
private final Map backend;
private JavaBasedImmutableMap(LinkedHashMap backend) {
this.backend = unmodifiableMap(backend);
}
@Override
public Map toMap() {
return copy();
}
@Override
public int size() {
return backend.size();
}
@Override
public ImmutableMap put(K key, V value) {
LinkedHashMap newMap = copy();
newMap.put(key, value);
return new JavaBasedImmutableMap<>(newMap);
}
@Override
public ImmutableMap putAll(ImmutableMap extends K, ? extends V> other) {
LinkedHashMap newMap = copy();
newMap.putAll(other.toMap());
return new JavaBasedImmutableMap<>(newMap);
}
@Override
public ImmutableMap remove(K key) {
LinkedHashMap newMap = copy();
newMap.remove(key);
return new JavaBasedImmutableMap<>(newMap);
}
@Override
public Option get(K key) {
return Option.of(() -> backend.get(key));
}
@Override
public ImmutableMap merge(K key, V value, Operator2 merger) {
LinkedHashMap newMap = copy();
newMap.merge(key, value, merger::apply);
return new JavaBasedImmutableMap<>(newMap);
}
@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 hashCode() {
return Objects.hash(backend);
}
@Override
public boolean equals(Object obj) {
return EQUAL.applyTo(this, obj);
}
@Override
public String toString() {
return "ImmutableMap(" + backend + ")";
}
private LinkedHashMap copy() {
return new LinkedHashMap<>(backend);
}
}
}
interface ImmutableTreeModule {
static Collector> toTreeMap(
Function1 super T, ? extends K> keyMapper,
Function1 super T, ? extends V> valueMapper) {
return Collectors.toMap(keyMapper::apply, valueMapper::apply, throwingMerge(), TreeMap::new);
}
static Collector> toLinkedHashMap(
Function1 super T, ? extends K> keyMapper,
Function1 super T, ? extends V> valueMapper) {
return Collectors.toMap(keyMapper::apply, valueMapper::apply, throwingMerge(), LinkedHashMap::new);
}
static BinaryOperator throwingMerge() {
return (a, b) -> { throw new IllegalArgumentException("conflict detected"); };
}
} © 2015 - 2025 Weber Informatics LLC | Privacy Policy