net.yetamine.lang.collections.Mapping Maven / Gradle / Ivy
Show all versions of net.yetamine.lang Show documentation
/*
* Copyright 2016 Yetamine
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.yetamine.lang.collections;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* A subset of the common {@link Map} interface that provides no bulk operations
* (except for {@link #clear()}) nor content enumeration.
*
*
* Because this interface is more abstract, it is suitable for encapsulating
* even non-volatile and distributed storages with associative structure. It
* mimics {@link Map} interface precisely though, so in many cases both
* interfaces are interchangeable.
*
*
* Implementations may tolerate {@code null} keys and values, but {@code null}
* value should be understood as missing association to the given key (this is
* similar to usual {@code Map} handling).
*
* @param
* the type of keys
* @param
* the type of values
*/
public interface Mapping {
/**
* Returns an instance that stores nothing and always returns {@code null}.
*
* @param
* the type of keys
* @param
* the type of values
*
* @return the instance of an empty immutable instance
*/
static Mapping empty() {
return EmptyMapping.INSTANCE.cast();
}
/**
* Returns a new instance based on the given {@link Map} instance.
*
*
* The result inherits all characteristics (like thread safety) from the
* given map which is used as the underlying storage.
*
* @param
* the type of keys
* @param
* the type of values
* @param map
* the map to use as the underlying storage. It must not be
* {@code null}.
*
* @return the instance using the given map
*/
static Mapping adapting(Map map) {
return new MappingAdapter<>(map);
}
// Reading interface
/**
* Returns the value associated with the given key.
*
* @param key
* the key whose associated value is to be returned
*
* @return the value associated to the key, or {@code null} when no such
* value exists
*
* @see Map#get(Object)
*/
V get(Object key);
/**
* Returns the value to which the specified key is mapped, or the given
* default value if this map contains no mapping for the key.
*
*
* This method is a convenient equivalent (which may be more efficient) for
* {@code find(key).orElse(value)}. The {@link #find(Object)} version might
* be more descriptive though.
*
* @param key
* the key whose associated value is to be returned
* @param defaultValue
* the default mapping of the key
*
* @return the value to which the specified key is mapped, or the default
* mapping if no mapping for the key exists
*
* @see Map#getOrDefault(Object, Object)
*/
default V getOrDefault(Object key, V defaultValue) {
final V result = get(key);
return (result != null) ? result : defaultValue;
}
/**
* Returns the value to which the specified key is mapped, or uses the given
* provider to provide the result instead.
*
*
* This method is a convenient equivalent (which may be more efficient) for
* {@code find(key).orElseGet(supplier)}. The {@link #find(Object)} version
* might be more descriptive though.
*
* @param key
* the key whose associated value is to be returned
* @param provider
* the default value provider. It must not be {@code null}.
*
* @return the value to which the specified key is mapped, or the result of
* the given provider if no mapping for the key exists
*/
default V getOrProvide(Object key, Supplier extends V> provider) {
final V result = get(key);
return (result != null) ? result : provider.get();
}
/**
* Returns the value associated with the given key.
*
*
* This method is a shortcut for {@code Optional.ofNullable(map.get(key))}.
* It therefore does not work very well for actual {@code null} values, but
* it is great for simple actions on valid objects using following pattern:
* {@code map.find(key).ifPresent(consumer)}.
*
* @param key
* the key to use for looking up the value
*
* @return the value associated with the given key, or an empty container if
* no such value exists
*/
default Optional find(Object key) {
return Optional.ofNullable(get(key));
}
// Insertion methods
/**
* Associates the specified value with the specified key, or updates the
* existing association of the key with the given value.
*
* @param key
* the key with which the specified value is to be associated
* @param value
* the value to be associated with the specified key. For
* {@code null} values, the operation outcome may differ for
* various implementations and therefore {@code null} values
* should not be used with this method.
*
* @return the value of the previous association
*
* @see Map#put(Object, Object)
*/
V put(K key, V value);
/**
* Associates the specified key with the specified value if the key is not
* already associated with a value.
*
* @param key
* the key with which the specified value is to be associated
* @param value
* the value to associate with the key
*
* @return the previously associated value (which is retained), or
* {@code null} if the given value has been associated with the
* given key
*
* @see Map#putIfAbsent(java.lang.Object, java.lang.Object)
*/
V putIfAbsent(K key, V value);
/**
* Associates the specified value with the specified key, or removes the
* existing associations if the value is {@code null}.
*
*
* This method calls {@link #put(Object, Object)} or {@link #remove(Object)}
* depending on the value, which makes easy to use {@code null} values even
* with implementations that do not support {@code null} values.
*
* @param key
* the key with which the specified value is to be associated
* @param value
* the value to be associated with the specified key, or
* {@code null} for removing the association for the key
*
* @return the value of the previous association
*/
default V let(K key, V value) {
return (value != null) ? put(key, value) : remove(key);
}
// Replacement methods
/**
* Replaces the entry for the specified key only if the key is currently
* associated with the specified value.
*
* @param key
* the key with which the specified value is to be associated
* @param value
* the value to be associated with the specified key
*
* @return the previous value, or {@code null} if none
*
* @see Map#replace(java.lang.Object, java.lang.Object)
*/
V replace(K key, V value);
/**
* Replaces the entry for the specified key only if the key is currently
* associated with the specified value.
*
* @param key
* the key with which the specified value is to be associated
* @param oldValue
* the expected currently associated value
* @param newValue
* the value to be associated with the specified key
*
* @return {@code true} if the value was replaced, {@code false} if the
* expected value didn't match the actual current value
*
* @see Map#replace(Object, Object, Object)
*/
boolean replace(K key, V oldValue, V newValue);
// Removal methods
/**
* Requests clearing all associations.
*
* @see Map#clear()
*/
void clear();
/**
* Removes the association for the specified key and returns the previously
* associated value if any.
*
* @param key
* the key whose associated value is to be removed and returned
*
* @return the value associated to the key, or {@code null} when no such
* value existed
*
* @see Map#remove(Object)
*/
V remove(Object key);
/**
* Removes the association for the specified key only if it is currently
* mapped to the specified value.
*
* @param key
* the key with which the specified value is associated
* @param value
* the value expected to be associated with the specified key
*
* @return {@code true} if the value was removed
*
* @see Map#remove(java.lang.Object, java.lang.Object)
*/
boolean remove(Object key, Object value);
// Composite operations
/**
* Associates the specified key, if the key is not already associated with a
* value, with the given value; otherwise, replaces the associated value
* with the results of the given remapping function, or removes if the
* result is {@code null}.
*
* @param key
* the key with which the specified value is associated
* @param value
* the value to associate the key with
* @param remapping
* the function to compute a value. It must not be {@code null}.
*
* @return the new value associated with the specified key, or {@code null}
* if no value is associated with the key
*
* @see java.util.Map#merge(java.lang.Object, java.lang.Object,
* java.util.function.BiFunction)
*/
V merge(K key, V value, BiFunction super V, ? super V, ? extends V> remapping);
/**
* Attempts to compute a mapping for the specified key and its current
* mapped value (or {@code null} if there is no current mapping).
*
* @param key
* the key with which the specified value is to be associated
* @param remapping
* the function to compute a value. It must not be {@code null}.
*
* @return the new value associated with the specified key, or {@code null}
* if none
*
* @see Map#compute(java.lang.Object, java.util.function.BiFunction)
*/
V compute(K key, BiFunction super K, ? super V, ? extends V> remapping);
/**
* Returns the value associated with the given key, or uses the given
* mapping function to compute the result (the result may be stored).
*
* @param key
* the key whose associated value is to be returned
* @param mapping
* the mapping function for supplying the result for the given
* key. It must not be {@code null}.
*
* @return the value associated with the given key, or the result of the
* mapping function if no such value existed before calling this
* method
*
* @see Map#computeIfAbsent(Object, Function)
*/
V computeIfAbsent(K key, Function super K, ? extends V> mapping);
/**
* Sets the value associated with the given key to the result of the given
* remapping function.
*
* @param key
* the key whose associated value is to be returned
* @param remapping
* the remapping function for supplying the result for the given
* key. It must not be {@code null}.
*
* @return the value associated with the given key
*
* @see Map#computeIfPresent(Object, BiFunction)
*/
V computeIfPresent(K key, BiFunction super K, ? super V, ? extends V> remapping);
/**
* Returns the value associated with the given key, or uses the given
* supplier to compute the result (the result may be stored).
*
* @param key
* the key of the association to compute
* @param valueSupplier
* the supplier to provide the value. It must not be
* {@code null}.
*
* @return the value associated with the given key, or the result of the
* supplier if no such value existed before calling this method
*/
default V supplyIfAbsent(K key, Supplier extends V> valueSupplier) {
return computeIfAbsent(key, k -> valueSupplier.get());
}
/**
* Sets the value if the mapping is present using a supplier.
*
* @param key
* the key of the association to compute
* @param valueSupplier
* the supplier to provide the value
*
* @return the computed value
*/
default V supplyIfPresent(K key, Supplier extends V> valueSupplier) {
return computeIfPresent(key, (k, v) -> valueSupplier.get());
}
}
/**
* A singleton implementing an empty {@link Mapping}.
*/
enum EmptyMapping implements Mapping