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

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

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

import com.ajjpj.afoundation.collection.AEquality;
import com.ajjpj.afoundation.collection.tuples.ATuple2;
import com.ajjpj.afoundation.function.AFunction1;

import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;


/**
 * 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 extends AbstractAMap implements AMapEntry { 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 AListMap 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(ATuple2 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 AEquality keyEquality () { return equality; } @Override public AMap clear () { return empty (equality); } @Override public int size() { return 0; } @Override public AOption get(K key) { return AOption.none(); } @Override public K getKey() { throw new NoSuchElementException("empty map"); } @Override public V getValue() { throw new NoSuchElementException("empty map"); } @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() { return null; } @Override public Iterator> iterator() { return new ListMapIterator<>(this); } @Override public ASet keys() { return new AListSet<> (this); } 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 K getKey() { return key; } @Override public V getValue() { return value; } @Override public AListMap tail() { return tail; } @Override public int size() { int result = 0; AListMap m = this; while(m.tail() != null) { m = m.tail(); result += 1; } return result; } @Override public AOption get(K key) { AListMap m = this; while(m.nonEmpty()) { if(equality.equals(m.getKey (), key)) { return AOption.some(m.getValue ()); } m = m.tail(); } return AOption.none(); } @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) { int idx = 0; AListMap remaining = this; while(remaining.nonEmpty()) { if(equality.equals(remaining.getKey (), key)) { remaining = remaining.tail(); break; } idx += 1; remaining = remaining.tail(); } AListMap result = remaining; AListMap iter = this; for (int i=0; i (iter.getKey (), iter.getValue (), result); iter = iter.tail (); } if (idx >= size ()) { return this; } return result; } } static class ListMapIterator implements Iterator> { private AListMap pos; ListMapIterator(AListMap pos) { this.pos = pos; } @Override public boolean hasNext() { return pos.nonEmpty(); } @Override public AMapEntry next() { final AMapEntry result = pos; pos = pos.tail(); return result; } @Override public void remove() { throw new UnsupportedOperationException(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy