convex.core.data.AMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of convex-core Show documentation
Show all versions of convex-core Show documentation
Convex core libraries and common utilities
The newest version!
package convex.core.data;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import convex.core.data.type.AType;
import convex.core.data.type.Types;
import convex.core.data.util.BlobBuilder;
import convex.core.exceptions.TODOException;
import convex.core.lang.RT;
import convex.core.util.Errors;
import convex.core.util.Utils;
/**
* Abstract base class for maps.
*
* Maps are Smart Data Structures that represent an immutable mapping of keys to
* values. The can also be seen as a data structure where the elements are map entries
* (equivalent to length 2 vectors)
*
* Ordering of map entries (as seen through iterators etc.) depends on map type.
*
* @param Type of keys
* @param Type of values
*/
public abstract class AMap extends ADataStructure>
implements Map, IAssociative {
protected AMap(long count) {
super(count);
}
@Override
public AType getType() {
return Types.MAP;
}
/**
* Gets the values from this map, in map-determined order
*/
@Override
public AVector values() {
int len = size();
ArrayList al = new ArrayList(len);
accumulateValues(al);
return Vectors.create(al);
}
// TODO: Review plausible alternative implementation for values()
//
// @Override
// public AVector values() {
// return reduceValues((v,e)->((AVector)v).append(e), Vectors.empty());
// }
/**
* Associates the given key with the specified value.
*
* @param key Map key to associate
* @param value Map value
* @return An updated map with the new association, or null if the association fails
*/
public abstract AMap assoc(ACell key, ACell value);
/**
* Dissociates a key from this map, returning an updated map if the key was
* removed, or the same unchanged map if the key is not present.
*
* @param key Key to remove.
* @return Updated map
*/
public abstract AMap dissoc(ACell key);
public final boolean containsKeyRef(Ref ref) {
return getKeyRefEntry(ref) != null;
}
@SuppressWarnings("unchecked")
public boolean containsKey(ACell key) {
return getEntry((K)key)!=null;
}
@Override
public final boolean containsKey(Object key) {
if ((key==null)||(key instanceof ACell)) {
return containsKey((ACell)key);
}
// If not a valid CVM value, cannot contain key
return false;
}
@Override
public final boolean containsValue(Object value) {
if (value instanceof ACell) return containsValue((ACell)value);
return false;
}
/**
* CHecks if this map contains the given value. WARNING: probably O(n)
* @param value Value to check
* @return true if map contains value, false otherwise
*/
public abstract boolean containsValue(ACell value);
/**
* Get an entry given a Ref to the key value. This is more efficient than
* directly looking up using the key for some map types, and should be preferred
* if the caller already has a Ref available.
*
* @param ref Ref to Map key
* @return MapEntry for the given key ref
*/
public abstract MapEntry getKeyRefEntry(Ref ref);
/**
* Accumulate all entries from this map in the given mutable Set.
*
* @param h HashSet in which to accumulate entries
*/
protected abstract void accumulateEntrySet(Set> h);
/**
* Accumulate all keys from this map in the given mutable Set.
*
* @param h HashSet in which to accumulate keys
*/
protected abstract void accumulateKeySet(Set h);
/**
* Accumulate all values from this map in the given mutable list.
*
* @param al ArrayList in which to accumulate values
*/
protected abstract void accumulateValues(java.util.List al);
@Override
public final V put(K key, V value) {
throw new UnsupportedOperationException(Errors.immutable(this));
}
@Override
public final V remove(Object key) {
throw new UnsupportedOperationException(Errors.immutable(this));
}
@Override
public final void putAll(Map extends K, ? extends V> m) {
throw new UnsupportedOperationException(Errors.immutable(this));
}
@Override
public final void clear() {
throw new UnsupportedOperationException(Errors.immutable(this));
}
@Override
public abstract void forEach(BiConsumer super K, ? super V> action);
@Override
public boolean print(BlobBuilder sb, long limit) {
sb.append('{');
long n=count();
for (long i=0; i e=entryAt(i);
K k=e.getKey();
if (!RT.print(sb,k,limit)) return false;
sb.append(' ');
V v=e.getValue();
if (!RT.print(sb,v,limit)) return false;
if (i<(n-1)) sb.append(',');
}
sb.append('}');
return sb.check(limit);
}
/**
* Associate the given map entry into the map. May return null if the map entry is not valid for this map type.
*
* @param e A map entry
* @return The updated map
*/
public abstract AMap assocEntry(MapEntry e);
/**
* Gets the entry in this map at a specified index, according to the
* map-specific order. Caller responsible for bounds check!
*
* @param i Index of entry
* @return MapEntry at the specified index.
*/
public abstract MapEntry entryAt(long i);
@Override
public Ref> getElementRef(long index) {
checkIndex(index);
return entryAt(index).getRef();
}
/**
* Gets the map entry at the specified position in this Map
*
* @param i Index of entry to get
* @return map entry
*/
@Override
public final MapEntry get(long i) {
checkIndex(i);
return entryAt(i);
}
/**
* Gets the MapEntry for the given key
*
* @param k Key to lookup in Map
* @return The map entry, or null if the key is not found
*/
public abstract MapEntry getEntry(ACell k);
@Override
public final V get(Object key) {
if ((key==null)||(key instanceof ACell)) return (V) get((ACell)key);
return null;
}
public abstract V get(ACell key);
/**
* Gets the value at a specified key, or returns the fallback value if not found
*
* @param key Key to lookup in Map
* @param notFound Fallback value to return if key is not present
* @return Value for the specified key, or the notFound value.
*/
@SuppressWarnings("unchecked")
public final V get(ACell key, ACell notFound) {
MapEntry me = getEntry((K) key);
if (me == null) {
return (V) notFound;
} else {
return me.getValue();
}
}
/**
* Reduce over all values in this map
*
* @param Type of reduction return value
* @param func A function taking the reduction value and a map value
* @param initial Initial reduction value
* @return The final reduction value
*/
public abstract R reduceValues(BiFunction super R, ? super V, ? extends R> func, R initial);
/**
* Filters all values in this map with the given predicate.
*
* @param pred A predicate specifying which elements to retain.
* @return The updated map containing those entries where the predicate returned
* true.
*/
public AMap filterValues(Predicate pred) {
throw new TODOException();
}
/**
* Reduce over all map entries in this map
*
* @param Type of reduction return value
* @param func A function taking the reduction value and a map entry
* @param initial Initial reduction value
* @return The final reduction value
*/
public abstract R reduceEntries(BiFunction super R, MapEntry, ? extends R> func, R initial);
@SuppressWarnings("unchecked")
@Override
public Set keySet() {
ASet ks=reduceEntries((s,me)->s.conj(me.getKey()), (ASet)(Sets.empty()));
return ks;
}
/**
* Gets the map entry with the specified hash
*
* @param hash Hash of key to lookup
* @return The specified MapEntry, or null if not found.
*/
protected abstract MapEntry getEntryByHash(Hash hash);
/**
* Adds a new map entry to this map. The argument must be a valid map entry or
* length 2 vector.
*
* @param x An object that can be cast to a MapEntry
* @return Updated map with the specified entry added, or null if the argument
* is not a valid map entry
*/
public AMap conj(ACell x) {
MapEntry me = RT.ensureMapEntry(x);
if (me == null) return null;
return assocEntry(me);
}
/**
* Gets a vector of all map entries.
*
* @return Vector map entries, in map-defined order.
*/
public AVector> entryVector() {
return reduceEntries((acc, e) -> acc.conj(e), Vectors.empty());
}
/**
* Merge another map into this map. Replaces existing entries if they are
* different.
*
* O(n) in size of map to merge. Preserves the type of the current map.
*
* @param m A map to merge into this map
* @return The updated map, or null if convesrion fails
*/
public AMap merge(AMap m) {
AMap result = this;
long n = m.count();
for (int i = 0; i < n; i++) {
result = result.assocEntry(m.entryAt(i));
if (result==null) return null;
}
return result;
}
@Override
public AMap slice(long start) {
return slice(start,count);
}
@Override
public abstract AMap empty();
@Override
public AMap slice(long start, long end) {
AMap r=empty();
for (long i=start; i) r.conj(get(i));
if (r==null) return null;
}
return r;
}
/**
* Gets a vector of keys for this Map.
* O(n) in general.
*
* @return Vector of Keys
*/
public AVector getKeys() {
int n=Utils.checkedInt(count);
ACell[] keys=new ACell[n];
for (int i=0; i