
org.cp.elements.data.caching.Cache Maven / Gradle / Ivy
/*
* Copyright 2016 Author or Authors.
*
* 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 org.cp.elements.data.caching;
import static org.cp.elements.util.ArrayUtils.nullSafeArray;
import static org.cp.elements.util.CollectionUtils.nullSafeIterable;
import static org.cp.elements.util.MapUtils.nullSafeMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.cp.elements.lang.Assert;
import org.cp.elements.lang.Identifiable;
import org.cp.elements.lang.Nameable;
import org.cp.elements.lang.annotation.NullSafe;
import org.cp.elements.util.ArrayUtils;
import org.cp.elements.util.CollectionUtils;
/**
* The {@link Cache} interface is an Abstract Data Type (ADT) defining a cache data structure,
* mapping keys to values in-memory for quick access.
*
* Caches are used in use cases when, given identical input, the data access operation returns
* the same output.
*
* @author John Blum
* @param {@link Class type} of the keys used by this {@link Cache}.
* @param {@link Class type} of the values stored by this {@link Cache}.
* @see java.lang.Comparable
* @see java.lang.Iterable
* @see java.util.Map
* @see org.cp.elements.data.caching.provider.ConcurrentMapCache
* @see org.cp.elements.data.caching.AbstractCache
* @see org.cp.elements.lang.Identifiable
* @see org.cp.elements.lang.Nameable
* @since 1.0.0
*/
@SuppressWarnings("unused")
public interface Cache, VALUE> extends Iterable, Nameable {
/**
* Determines whether this {@link Cache} contains any entries.
*
* @return a boolean value indicating whether this {@link Cache} contains any entries.
* @see #size()
*/
@NullSafe
default boolean isEmpty() {
return size() == 0;
}
/**
* Clears the entire contents (all entries) of this {@link Cache}.
*
* @see #evictAll(Iterable)
* @see #keys()
*/
@NullSafe
default void clear() {
evictAll(keys());
}
/**
* Determines whether this {@link Cache} contains an entry mapped with the given {@link KEY key}.
*
* @param key {@link KEY key} to evaluate.
* @return a boolean value indicating whether this {@link Cache} contains an entry
* mapped with the given {@link KEY key}.
*/
boolean contains(KEY key);
/**
* Determines whether this {@link Cache} contains entries for each and every {@link KEY key}.
*
* @param keys {@link KEY keys} to evaluate.
* @return a boolean value indicating whether this {@link Cache} contains entries for each and every
* given {@link KEY key}.
* @see #containsAll(Iterable)
* @see #contains(Comparable)
*/
@NullSafe
@SuppressWarnings("unchecked")
default boolean containsAll(KEY... keys) {
return ArrayUtils.isNotEmpty(keys) && Arrays.stream(nullSafeArray(keys)).allMatch(this::contains);
}
/**
* Determines whether this {@link Cache} contains entries for each and every {@link KEY key}.
*
* @param keys {@link KEY keys} to evaluate.
* @return a boolean value indicating whether this {@link Cache} contains entries for each and every
* given {@link KEY key}.
* @see #containsAll(Comparable[])
* @see #contains(Comparable)
* @see java.lang.Iterable
*/
@NullSafe
default boolean containsAll(Iterable keys) {
return CollectionUtils.isNotEmpty(keys)
&& StreamSupport.stream(nullSafeIterable(keys).spliterator(), false).allMatch(this::contains);
}
/**
* Determines whether this {@link Cache} contains at least 1 entry mapped with any of the given {@link KEY keys}.
*
* @param keys {@link KEY keys} to evaluate.
* @return a boolean value indicating whether this {@link Cache} contains at least 1 entry
* mapped with any of the given {@link KEY keys}.
* @see #containsAny(Iterable)
* @see #contains(Comparable)
*/
@NullSafe
@SuppressWarnings("unchecked")
default boolean containsAny(KEY... keys) {
return Arrays.stream(nullSafeArray(keys)).anyMatch(this::contains);
}
/**
* Determines whether this {@link Cache} contains at least 1 entry mapped with any of the given {@link KEY keys}.
*
* @param keys {@link KEY keys} to evaluate.
* @return a boolean value indicating whether this {@link Cache} contains at least 1 entry
* mapped with any of the given {@link KEY keys}.
* @see #containsAny(Iterable)
* @see #contains(Comparable)
* @see java.lang.Iterable
*/
@NullSafe
default boolean containsAny(Iterable keys) {
return StreamSupport.stream(nullSafeIterable(keys).spliterator(), false).anyMatch(this::contains);
}
/**
* Removes the entry mapped to the given {@link KEY key} from this {@link Cache}.
*
* @param key {@link KEY key} identifying the entry to remove from this {@link Cache}.
*/
void evict(KEY key);
/**
* Removes all the entries mapped to the given {@link KEY keys} from this {@link Cache}.
*
* @param keys array of {@link KEY keys} identifying entries to remove from this {@link Cache}.
* @see #evict(Comparable)
* @see #evictAll(Comparable[])
*/
@SuppressWarnings("unchecked")
default void evictAll(KEY... keys) {
Arrays.stream(nullSafeArray(keys)).forEach(this::evict);
}
/**
* Removes all the entries mapped to the given {@link KEY keys} from this {@link Cache}.
*
* @param keys {@link Iterable} of {@link KEY keys} identifying entries to remove from this {@link Cache}.
* @see #evict(Comparable)
* @see #evictAll(Comparable[])
* @see java.lang.Iterable
*/
@SuppressWarnings("unchecked")
default void evictAll(Iterable keys) {
StreamSupport.stream(nullSafeIterable(keys).spliterator(), false).forEach(this::evict);
}
/**
* Caches all entries from given {@link Map} in this {@link Cache}.
*
* @param map {@link Map} containing the entries to cache.
* @see #put(Comparable, Object)
* @see java.util.Map
*/
default void from(Map map) {
nullSafeMap(map).forEach(this::put);
}
/**
* Gets the {@link VALUE value} mapped to the given {@link KEY} in this {@link Cache}.
*
* Returns {@literal null} if the {@link VALUE value} for the given {@link KEY key} is {@literal null},
* or this {@link Cache} does not contain an entry with the given {@link KEY key}.
*
* @param key {@link KEY key} used to lookup the desired {@link VALUE value}.
* @return the {@link VALUE value} mapped to the given {@link KEY key}.
* @see #put(Comparable, Object)
*/
VALUE get(KEY key);
/**
* Gets all the {@link VALUE values} in this {@link Cache} mapped to the given {@link KEY keys}.
*
* Returns an empty {@link List} if the array of {@link KEY keys} is {@literal null} or {@literal empty}.
*
* Given an array of {@link KEY keys}: {@literal keyOne}, {@literal keyTwo}, ..., {@literal keyN}, this method
* will return a {@link List} of {@link VALUE values}: {@literal valueOne}, {@literal valueTwo}, ...,
* {@literal valueN} even if the values are {@literal null}. The {@link List} of {@link VALUE values}
* matches 1 for 1 for each key in the array in the order that the {@link KEY keys} are given.
*
* @param keys {@link KEY keys} used to lookup the desired {@link VALUE values}.
* @return a {@link List} of {@link VALUE values} for all the given {@link KEY keys}
* in the order given by the {@link KEY keys}.
* @see #get(Comparable)
* @see java.util.List
*/
@NullSafe
@SuppressWarnings("unchecked")
default List getAll(KEY... keys) {
return Arrays.stream(nullSafeArray(keys)).map(this::get).collect(Collectors.toList());
}
/**
* Gets all the {@link VALUE values} in this {@link Cache} mapped to the given {@link KEY keys}.
*
* Returns an empty {@link List} if the {@link Iterable} of {@link KEY keys} is {@literal null} or {@literal empty}.
*
* Given an {@link Iterable} of {@link KEY keys}: {@literal keyOne}, {@literal keyTwo}, ..., {@literal keyN},
* this method will return a {@link List} of {@link VALUE values}: {@literal valueOne}, {@literal valueTwo}, ...,
* {@literal valueN} even if the values are {@literal null}. The {@link List} of {@link VALUE values}
* matches 1 for 1 for each key in the {@link Iterable} in the order that the {@link KEY keys} are given.
*
* @param keys {@link KEY keys} used to lookup the desired {@link VALUE values}.
* @return a {@link List} of {@link VALUE values} for all the given {@link KEY keys}
* in the order given by the {@link KEY keys}.
* @see #get(Comparable)
* @see java.lang.Iterable
* @see java.util.List
*/
@NullSafe
default List getAll(Iterable keys) {
return StreamSupport.stream(nullSafeIterable(keys).spliterator(), false).map(this::get)
.collect(Collectors.toList());
}
/**
* Returns all the {@link KEY keys} in this {@link Cache}.
*
* @return a {@link Set} containing all of the {@link KEY keys} in this {@link Cache}.
* @see java.util.Set
*/
@NullSafe
Set keys();
/**
* Puts the given {@link VALUE value} mapped to the given {@link KEY key} into this {@link Cache}.
*
* @param key {@link KEY} used to map the {@link VALUE value}; must not be {@literal null}.
* @param value {@link VALUE} put into this {@link Cache} mapped to the given {@link KEY key}.
* @throws IllegalArgumentException if {@link KEY key} is {@literal null}.
* @see #get(Comparable)
*/
void put(KEY key, VALUE value);
/**
* Puts the given {@link Identifiable} object into this {@link Cache} by mapping the {@link Identifiable#getId() ID}
* of the {@link Identifiable} object to the {@link Identifiable} object itself.
*
* The {@link Identifiable} object must be an instance of {@link VALUE}.
*
* @param entity {@link Identifiable} object to put into this {@link Cache}.
* @throws ClassCastException if the {@link Identifiable} object is not an instance of {@link VALUE}.
* @throws IllegalArgumentException if the {@link Identifiable} object or its {@link Identifiable#getId() ID}
* is {@literal null}.
* @see org.cp.elements.lang.Identifiable
* @see #put(Comparable, Object)
*/
@SuppressWarnings("unchecked")
default void put(Identifiable entity) {
Assert.notNull(entity, "Entity is required");
Assert.notNull(entity.getId(), "Entity ID is required");
put(entity.getId(), (VALUE) entity);
}
/**
* Puts all the {@link Identifiable} objects into this {@link Cache} by mapping each {@link Identifiable} object's
* {@link Identifiable#getId() ID} to the {@link Identifiable} object itself.
*
* All {@link Identifiable} objects must be an instance of {@link VALUE}.
*
* Warning: this putAll(..) operation is not atomic.
*
* For example, if any {@link Identifiable} object in the array is {@literal null}, then it will cause
* an {@link IllegalArgumentException} to be thrown. However, any {@link Identifiable} object that came before
* the {@literal null} {@link Identifiable} object will still be persisted to this {@link Cache}.
*
* @param entities array of {@link Identifiable} objects to put into this {@link Cache}.
* @throws ClassCastException if any {@link Identifiable} object is not an instance of {@link VALUE}.
* @throws IllegalArgumentException if any {@link Identifiable} object or their {@link Identifiable#getId() IDs}
* are {@literal null}.
* @see org.cp.elements.lang.Identifiable
* @see #put(Comparable, Object)
*/
@SuppressWarnings("unchecked")
default void putAll(Identifiable... entities) {
Arrays.stream(nullSafeArray(entities, Identifiable.class)).forEach(this::put);
}
/**
* Puts all the {@link Identifiable} objects into this {@link Cache} by mapping each {@link Identifiable} object's
* {@link Identifiable#getId() ID} to the {@link Identifiable} object itself.
*
* All {@link Identifiable} objects must be an instance of {@link VALUE}.
*
* Warning: this putAll(..) operation is not atomic.
*
* For example, if any {@link Identifiable} object in the iteration is {@literal null}, then it will cause
* an {@link IllegalArgumentException} to be thrown. However, any {@link Identifiable} object that came before
* the {@literal null} {@link Identifiable} object will still be persisted to this {@link Cache}.
*
* @param entities {@link Iterable} of {@link Identifiable} objects to put into this {@link Cache}.
* @throws ClassCastException if any {@link Identifiable} object is not an instance of {@link VALUE}.
* @throws IllegalArgumentException if any {@link Identifiable} object or their {@link Identifiable#getId() IDs}
* are {@literal null}.
* @see org.cp.elements.lang.Identifiable
* @see #put(Comparable, Object)
* @see java.lang.Iterable
*/
@SuppressWarnings("unchecked")
default void putAll(Iterable> entities) {
StreamSupport.stream(nullSafeIterable(entities).spliterator(), false).forEach(this::put);
}
/**
* Puts the given {@link KEY key} and {@link VALUE value} in this {@link Cache}
* iff an entry with given {@link KEY key} does not already exists.
*
* @param key {@link KEY} used to map the {@link VALUE value} if not already present; must not be {@literal null}.
* @param value {@link VALUE} to put into this {@link Cache} mapped to the given {@link KEY key}.
* @return the existing {@link VALUE value} if present, otherwise return {@literal null}.
* @throws IllegalArgumentException if {@link KEY key} is {@literal null}.
* @see #contains(Comparable)
* @see #put(Comparable, Object)
* @see #putIfPresent(Comparable, Object)
*/
default VALUE putIfAbsent(KEY key, VALUE value) {
Assert.notNull(key, "Key is required");
if (!contains(key)) {
put(key, value);
return null;
}
else {
return get(key);
}
}
/**
* Puts the given {@link Identifiable} object in this {@link Cache} iff the {@link Identifiable} object
* is not {@literal null} and the {@link Identifiable} object identified by its {@link Identifiable#getId() ID}
* is not already present in this {@link Cache}.
*
* @param entity {@link Identifiable} object to put into this {@link Cache} if not already present.
* @return the existing {@link VALUE value} if present, otherwise return {@literal null}.
* @throws ClassCastException if the {@link Identifiable} object is not an instance of {@link VALUE}.
* @see org.cp.elements.lang.Identifiable
* @see #contains(Comparable)
* @see #put(Comparable, Object)
* @see #putIfPresent(Identifiable)
*/
@SuppressWarnings("unchecked")
default VALUE putIfAbsent(Identifiable entity) {
Assert.notNull(entity, "Entity is required");
KEY entityId = entity.getId();
Assert.notNull(entityId, "Entity ID is required");
if (!contains(entityId)) {
put(entityId, (VALUE) entity);
return null;
}
else {
return get(entityId);
}
}
/**
* Puts the {@link VALUE value} in this {@link Cache} mapped to the given {@link KEY key} iff an entry
* with the given {@link KEY key} already exists in this {@link Cache}.
*
* @param key {@link KEY key} used to map the {@link VALUE new value} in this {@link Cache}.
* @param newValue {@link VALUE new value} replacing the existing value mapped to the given {@link KEY key}
* in this {@link Cache}.
* @return the existing {@link VALUE value} if present, otherwise return {@literal null}.
* @throws IllegalArgumentException if {@link KEY key} is {@literal null}.
* @see #contains(Comparable)
* @see #put(Comparable, Object)
* @see #putIfAbsent(Comparable, Object)
*/
default VALUE putIfPresent(KEY key, VALUE newValue) {
Assert.notNull(key, "Key is required");
if (contains(key)) {
VALUE existingValue = get(key);
put(key, newValue);
return existingValue;
}
return null;
}
/**
* Puts the given {@link Identifiable} object in this {@link Cache} iff if the {@link Identifiable} object
* is already present.
*
* @param newEntity {@link Identifiable} object replacing the existing object with the same {@link Identifiable#getId() ID}.
* @return the existing {@link VALUE value} if present, otherwise return {@literal null}.
* @throws ClassCastException if the {@link Identifiable} object is not an instance of {@link VALUE}.
* @see org.cp.elements.lang.Identifiable
* @see #contains(Comparable)
* @see #put(Comparable, Object)
* @see #putIfAbsent(Identifiable)
*/
@SuppressWarnings("unchecked")
default VALUE putIfPresent(Identifiable newEntity) {
Assert.notNull(newEntity, "Entity is required");
KEY entityId = newEntity.getId();
if (contains(entityId)) {
VALUE existingValue = get(entityId);
put(entityId, (VALUE) newEntity);
return existingValue;
}
return null;
}
/**
* Determines the number of entries contained in this {@link Cache}.
*
* @return an integer value with the number of entries contained in this {@link Cache}.
* @see #isEmpty()
*/
int size();
/**
* Returns this {@link Cache} as instance of {@link Map}.
*
* @return a {@link Map} containing all the entries in this {@link Cache}.
* @see java.util.Map
*/
default Map toMap() {
return keys().stream().collect(Collectors.toMap(key -> key, this::get));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy