All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.ajjpj.abase.collection.immutable.AListMap Maven / Gradle / Ivy

Go to download

a-base is a library of basic (hence the name) classes, most notably immutable collection classes with copy-on-write operations

The newest version!
package com.ajjpj.abase.collection.immutable;

import com.ajjpj.abase.collection.AEquality;
import com.ajjpj.abase.collection.APair;
import com.ajjpj.abase.function.AFunction1;

import java.io.Serializable;
import java.util.*;


/**
 * This AMap implementation stores entries in a linked list, giving all lookup operations O(n) complexity. That makes
 *  AHashMap the better choice most of the time.

* * This class is however useful when keys are known to have the same hash codes (e.g. deep in the innards of AHashMap), * or if control over iteration order is desirable. * * @author arno */ public class AListMap implements AMap, Serializable { private static final AEquality DEFAULT_EQUALITY = AEquality.EQUALS; private static final AListMap emptyEquals = new AListMap<>(AEquality.EQUALS); private static final AListMap emptyIdentity = new AListMap<>(AEquality.IDENTITY); /** * Returns an empty AListMap instance with default (i.e. equals-based) equalityForEquals. Calling this factory method instead * of the constructor allows internal reuse of empty map instances since they are immutable. */ public static AListMap empty() { return empty(DEFAULT_EQUALITY); } /** * Returns an empty AListMap instance with a given equalityForEquals. Calling this factory method instead * of the constructor allows internal reuse of empty map instances since they are immutable. */ @SuppressWarnings("unchecked") public static AListMap empty(AEquality equality) { if(equality == AEquality.EQUALS) return (AListMap) emptyEquals; if(equality == AEquality.IDENTITY) return (AListMap) emptyIdentity; return new AListMap<> (equality); } /** * Returns an AHashMap instance with default (i.e. equals-based) equalityForEquals, initializing it from separate 'keys' * and 'values' collections. Both collections are iterated exactly once, and are expected to have the same size. */ public static AListMap fromKeysAndValues(Iterable> elements) { return fromKeysAndValues(DEFAULT_EQUALITY, elements); } /** * Returns an AHashMap instance with a given equalityForEquals, initializing it from separate 'keys' * and 'values' collections. Both collections are iterated exactly once, and are expected to have the same size. */ public static AListMap fromKeysAndValues(AEquality equality, Iterable> elements) { AListMap result = empty(equality); for(APair el: elements) { result = result.updated(el._1, el._2); } return result; } public static AListMap fromKeysAndValues(Iterable keys, Iterable values) { return fromKeysAndValues(DEFAULT_EQUALITY, keys, values); } /** * Returns an AListMap instance with a given equalityForEquals, initializing it from separate 'keys' * and 'values' collections. Both collections are iterated exactly once, and are expected to have the same size. */ public static AListMap fromKeysAndValues(AEquality equality, Iterable keys, Iterable values) { final Iterator ki = keys.iterator(); final Iterator vi = values.iterator(); AListMap result = empty (equality); while(ki.hasNext()) { final K key = ki.next(); final V value = vi.next(); result = result.updated(key, value); } return result; } final AEquality equality; private Integer cachedHashcode = null; // intentionally not volatile - potentially recalculating for different threads is traded for better single threaded performance private AListMap(AEquality equality) { this.equality = equality; } @Override public int size() { return 0; } @Override public boolean isEmpty() { return true; } @Override public boolean nonEmpty() { return false; } @Override public AOption get(K key) { return AOption.none(); } @Override public V getRequired(K key) { return get(key).get(); } public K key() { throw new NoSuchElementException("empty map"); } public V value() { throw new NoSuchElementException("empty map"); } @Override public boolean containsKey(K key) { return get(key).isDefined(); } @Override public boolean containsValue(V value) { return false; } @Override public AListMap updated(K key, V value) { return new Node<>(key, value, this); } @Override public AListMap removed(K key) { return this; } public AListMap tail() { throw new NoSuchElementException("empty map"); } @Override public Map asJavaUtilMap() { return new JavaUtilMapWrapper<>(this); } @Override public Iterator> iterator() { return new ListMapIterator<>(this); } @Override public Set keys() { return new KeySet(); } @Override public Collection values() { return new ValueCollection(); } @SuppressWarnings({"NullableProblems", "unchecked", "SuspiciousToArrayCall"}) class KeySet implements Set { @Override public int size() { return AListMap.this.size(); } @Override public boolean isEmpty() { return size() == 0; } @Override public boolean contains(Object o) { return containsKey((K) o); } @Override public Iterator iterator() { final Iterator> lmi = AListMap.this.iterator(); return new Iterator() { @Override public boolean hasNext() { return lmi.hasNext(); } @Override public K next() { return lmi.next()._1; } @Override public void remove() { lmi.remove(); } }; } @Override public Object[] toArray() { return new ArrayList<>(this).toArray(); } @Override public T[] toArray(T[] a) { return new ArrayList<>(this).toArray(a); } @Override public boolean add(K k) { throw new UnsupportedOperationException(); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public boolean containsAll(Collection c) { for(Object o: c) { if(!contains(o)) { return false; } } return true; } @Override public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } } @SuppressWarnings({"NullableProblems", "unchecked", "SuspiciousToArrayCall"}) class ValueCollection implements Collection { @Override public int size() { return AListMap.this.size(); } @Override public boolean isEmpty() { return size() == 0; } @Override public boolean contains(Object o) { return containsValue((V) o); } @Override public Iterator iterator() { final Iterator> lmi = AListMap.this.iterator(); return new Iterator() { @Override public boolean hasNext() { return lmi.hasNext(); } @Override public V next() {return lmi.next()._2; } @Override public void remove() { lmi.remove(); } }; } @Override public Object[] toArray() { return new ArrayList<>(this).toArray(); } @Override public T[] toArray(T[] a) { return new ArrayList<>(this).toArray(a); } @Override public boolean add(V v) { throw new UnsupportedOperationException(); } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public boolean containsAll(Collection c) { for(Object o: c) { if(!contains(o)) { return false; } } return true; } @Override public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } } @Override public AMap withDefaultValue(V defaultValue) { return new AMapWithDefaultValue<>(this, defaultValue); } @Override public AMap withDefault(AFunction1 function) { return new AMapWithDefault<>(this, function); } @Override public String toString() { final StringBuilder result = new StringBuilder("{"); boolean first = true; for(APair el: this) { if(first) { first = false; } else { result.append(", "); } result.append(el._1).append(" -> ").append(el._2); } result.append("}"); return result.toString(); } @SuppressWarnings("unchecked") @Override public boolean equals(Object o) { if(o == this) { return true; } if(! (o instanceof AMap)) { return false; } final AMap other = (AMap) o; if(size() != other.size()) { return false; } for(APair el: this) { if(! equality.equals(other.get(el._1), AOption.some(el._2))) { return false; } } return true; } @Override public int hashCode() { if(cachedHashcode == null) { int result = 0; for(APair el: this) { result = result ^ (31*equality.hashCode(el._1) + equality.hashCode(el._2)); } cachedHashcode = result; } return cachedHashcode; } static class Node extends AListMap { private final K key; private final V value; private final AListMap tail; Node(K key, V value, AListMap tail) { super(tail.equality); this.key = key; this.value = value; this.tail = tail; } @Override public boolean isEmpty() { return false; } @Override public boolean nonEmpty() { return true; } @Override public K key() { return key; } @Override public V value() { return value; } @Override public AListMap tail() { return tail; } @Override public int size() { int result = 0; AListMap m = this; while(m.nonEmpty()) { m = m.tail(); result += 1; } return result; } @Override public AOption get(K key) { AListMap m = this; while(m.nonEmpty()) { if(equality.equals(m.key(), key)) { return AOption.some(m.value()); } m = m.tail(); } return AOption.none(); } @Override public boolean containsValue(V value) { return equality.equals(this.value, value) || tail().containsValue(value); } @Override public AListMap updated(K key, V value) { final AListMap m = removed(key); return new Node<>(key, value, m); } @Override public AListMap removed(K key) { AList> raw = AList.nil(); AListMap remaining = this; while(remaining.nonEmpty()) { if(! equality.equals(remaining.key(), key)) { raw = raw.cons(new APair<>(remaining.key(), remaining.value())); } remaining = remaining.tail(); } return AListMap.fromKeysAndValues(equality, raw.reverse().asJavaUtilList()); } } static class ListMapIterator implements Iterator> { private AListMap pos; ListMapIterator(AListMap pos) { this.pos = pos; } @Override public boolean hasNext() { return pos.nonEmpty(); } @Override public APair next() { final APair result = new APair<> (pos.key(), pos.value()); pos = pos.tail(); return result; } @Override public void remove() { throw new UnsupportedOperationException(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy