*/
package org.opendaylight.yangtools.triemap;
-import java.util.AbstractMap.SimpleImmutableEntry;
-import java.util.Iterator;
-import java.util.Map.Entry;
-import java.util.NoSuchElementException;
-import java.util.Optional;
+import com.google.common.base.VerifyException;
/**
- * Similar to Scala's ListMap. Stores a linked set of entries, guaranteed to contain unique entry keys.
+ * Similar to Scala's ListMap, this is a single-linked list of set of map entries. Aside from the java.util.Set
+ * contract, this class fulfills the requirements for an immutable map entryset.
*
* @author Robert Varga
*
* @param <K> the type of keys
* @param <V> the type of values
*/
-final class LNodeEntries<K, V> extends LNodeEntry<K, V> {
- // Modified during remove0 only
- private LNodeEntries<K, V> next;
+abstract class LNodeEntries<K, V> extends LNodeEntry<K, V> {
+ private static final class Single<K, V> extends LNodeEntries<K, V> {
+ Single(final K key, final V value) {
+ super(key, value);
+ }
- private LNodeEntries(final K k, final V v) {
- this(k, v, null);
+ @Override
+ LNodeEntries<K, V> next() {
+ return null;
+ }
}
- private LNodeEntries(final K k, final V v, final LNodeEntries<K, V> next) {
- super(k, v);
- this.next = next;
- }
+ private static final class Multiple<K, V> extends LNodeEntries<K, V> {
+ // Modified during remove only, otherwise final
+ LNodeEntries<K, V> next;
- static <K,V> LNodeEntries<K, V> map(final K k1, final V v1, final K k2, final V v2) {
- return new LNodeEntries<>(k1, v1, new LNodeEntries<>(k2, v2));
+ // Used in remove() only
+ Multiple(final LNodeEntries<K, V> entry) {
+ this(entry.getKey(), entry.getValue(), null);
+ }
+
+ Multiple(final K key, final V value, final LNodeEntries<K, V> next) {
+ super(key, value);
+ this.next = next;
+ }
+
+ @Override
+ LNodeEntries<K, V> next() {
+ return next;
+ }
}
- Optional<Entry<K, V>> maybeSingleton() {
- return next != null ? Optional.empty() : Optional.of(new SimpleImmutableEntry<>(key(), value()));
+ LNodeEntries(final K key, final V value) {
+ super(key, value);
}
- int size() {
- int sz = 1;
- for (LNodeEntries<?, ?> wlk = next; wlk != null; wlk = wlk.next) {
- sz++;
- }
- return sz;
+ static <K,V> LNodeEntries<K, V> map(final K k1, final V v1, final K k2, final V v2) {
+ return new Multiple<>(k1, v1, new Single<>(k2, v2));
}
- LNodeEntry<K, V> findEntry(final Equivalence<? super K> equiv, final K key) {
+ /**
+ * Return the remainder of this list. Useful for implementing Iterator-like contract. Null indicates there are no
+ * more entries.
+ *
+ * @return Remainder of this list, or null if nothing remains
+ */
+ abstract LNodeEntries<K, V> next();
+
+ final LNodeEntry<K, V> findEntry(final Equivalence<? super K> equiv, final K key) {
// We do not perform recursion on purpose here, so we do not run out of stack if the key hashing fails.
- LNodeEntries<K, V> head = this;
+ LNodeEntries<K, V> entry = this;
do {
- if (equiv.equivalent(head.key(), key)) {
- return head;
+ if (equiv.equivalent(entry.getKey(), key)) {
+ return entry;
}
- head = head.next;
- } while (head != null);
+ entry = entry.next();
+ } while (entry != null);
return null;
}
- LNodeEntries<K,V> insert(final K key, final V value) {
- return new LNodeEntries<>(key, value, this);
+ final LNodeEntries<K,V> insert(final K key, final V value) {
+ return new Multiple<>(key, value, this);
}
- LNodeEntries<K, V> replace(final LNodeEntry<K, V> entry, final V v) {
- return new LNodeEntries<>(entry.key(), v, remove(entry));
+ final LNodeEntries<K, V> replace(final LNodeEntry<K, V> entry, final V value) {
+ final LNodeEntries<K, V> removed;
+ return (removed = remove(entry)) == null ? new Single<>(entry.getKey(), value)
+ : new Multiple<>(entry.getKey(), value, removed);
}
- LNodeEntries<K, V> remove(final LNodeEntry<K, V> entry) {
+ final LNodeEntries<K, V> remove(final LNodeEntry<K, V> entry) {
if (entry == this) {
- return next;
+ return next();
}
- final LNodeEntries<K, V> ret = new LNodeEntries<>(key(), value());
+ // This will result in a list with a long tail, i.e last entry storing explicit null. Overhead is amortized
+ // against the number of entries. We do not retain chains shorter than two, so the worst-case overhead is
+ // half-a-reference for an entry.
+ final Multiple<K, V> ret = new Multiple<>(this);
- LNodeEntries<K, V> last = ret;
- LNodeEntries<K, V> cur = next;
+ Multiple<K, V> last = ret;
+ LNodeEntries<K, V> cur = next();
while (cur != null) {
- if (entry.equals(cur)) {
- last.next = cur.next;
+ // We cannot use equals() here, as it is wired to key equality and we must never compare entries based on
+ // that property. This method is intended to remove a known reference, so identity is what we want.
+ if (entry == cur) {
+ last.next = cur.next();
return ret;
}
- last.next = new LNodeEntries<>(cur.key(), cur.value());
- last = last.next;
- cur = cur.next;
- }
-
- throw new IllegalStateException("Entry " + entry + " not found in entries " + this);
- }
-
- Iterator<Entry<K, V>> iterator() {
- return new NodeIterator<>(this);
- }
-
- static final class NodeIterator<K,V> implements Iterator<Entry<K, V>> {
- private LNodeEntries<K, V> n;
-
- NodeIterator(final LNodeEntries<K, V> n) {
- this.n = n;
+ final Multiple<K, V> tmp = new Multiple<>(cur);
+ last.next = tmp;
+ last = tmp;
+ cur = cur.next();
}
- @Override
- public boolean hasNext() {
- return n != null;
- }
-
- @Override
- public Entry<K, V> next() {
- if (n == null) {
- throw new NoSuchElementException();
- }
-
- final Entry<K, V> res = new SimpleImmutableEntry<>(n.key(), n.value());
- n = n.next;
- return res;
- }
+ throw new VerifyException(String.format("Failed to find entry %s", entry));
}
}