package de.invesdwin.util.collections.concurrent; import java.lang.reflect.Array; import java.util.Collection; import java.util.Iterator; import java.util.Set; import javax.annotation.concurrent.NotThreadSafe; import de.invesdwin.util.bean.tuple.ImmutableEntry; import de.invesdwin.util.collections.ADelegateMap; import de.invesdwin.util.collections.iterable.buffer.BufferingIterator; /** * Boosts the iteration speed over the values by keeping a fast iterator instance that only gets modified when changes * to the map occur. * * The iterator returned from this map is also suitable for concurrent modification during iteration. */ @NotThreadSafe public abstract class AFastIterableDelegateMap<K, V> extends ADelegateMap<K, V> { //arraylist wins in raw iterator speed compared to bufferingIterator since no remove is needed, though we need protection against concurrent modification private BufferingIterator<Entry<K, V>> fastIterable; private boolean empty; private int size; private Entry<K, V>[] entryArray; private K[] keyArray; private V[] valueArray; private final Set<Entry<K, V>> entrySet = new Set<Entry<K, V>>() { @Override public int size() { return AFastIterableDelegateMap.this.size(); } @Override public boolean isEmpty() { return AFastIterableDelegateMap.this.isEmpty(); } @Override public boolean contains(final Object o) { return getDelegate().entrySet().contains(o); } @Override public Iterator<Entry<K, V>> iterator() { if (fastIterable == null) { fastIterable = new BufferingIterator<Entry<K, V>>(getDelegate().entrySet()); } return fastIterable.iterator(); } @Override public Object[] toArray() { return getDelegate().entrySet().toArray(); } @Override public <T> T[] toArray(final T[] a) { return getDelegate().entrySet().toArray(a); } @Override public boolean add(final Entry<K, V> e) { throw newUnmodifiableException(); } @Override public boolean remove(final Object o) { throw newUnmodifiableException(); } @Override public boolean containsAll(final Collection<?> c) { return getDelegate().entrySet().containsAll(c); } @Override public boolean addAll(final Collection<? extends Entry<K, V>> c) { throw newUnmodifiableException(); } @Override public boolean retainAll(final Collection<?> c) { throw newUnmodifiableException(); } @Override public boolean removeAll(final Collection<?> c) { throw newUnmodifiableException(); } @Override public void clear() { throw newUnmodifiableException(); } }; private final Set<K> keySet = new Set<K>() { @Override public int size() { return AFastIterableDelegateMap.this.size(); } @Override public boolean isEmpty() { return AFastIterableDelegateMap.this.isEmpty(); } @Override public boolean contains(final Object o) { return getDelegate().containsKey(o); } @Override public Iterator<K> iterator() { final Iterator<Entry<K, V>> iterator = entrySet.iterator(); return new Iterator<K>() { @Override public boolean hasNext() { return iterator.hasNext(); } @Override public K next() { return iterator.next().getKey(); } }; } @Override public Object[] toArray() { return getDelegate().keySet().toArray(); } @Override public <T> T[] toArray(final T[] a) { return getDelegate().keySet().toArray(a); } @Override public boolean add(final K e) { throw newUnmodifiableException(); } @Override public boolean remove(final Object o) { throw newUnmodifiableException(); } @Override public boolean containsAll(final Collection<?> c) { return getDelegate().keySet().containsAll(c); } @Override public boolean addAll(final Collection<? extends K> c) { throw newUnmodifiableException(); } @Override public boolean retainAll(final Collection<?> c) { throw newUnmodifiableException(); } @Override public boolean removeAll(final Collection<?> c) { throw newUnmodifiableException(); } @Override public void clear() { throw newUnmodifiableException(); } }; private final Collection<V> values = new Collection<V>() { @Override public int size() { return AFastIterableDelegateMap.this.size(); } @Override public boolean isEmpty() { return AFastIterableDelegateMap.this.isEmpty(); } @Override public boolean contains(final Object o) { return getDelegate().containsValue(o); } @Override public Iterator<V> iterator() { final Iterator<Entry<K, V>> iterator = entrySet.iterator(); return new Iterator<V>() { @Override public boolean hasNext() { return iterator.hasNext(); } @Override public V next() { return iterator.next().getValue(); } }; } @Override public Object[] toArray() { return getDelegate().values().toArray(); } @Override public <T> T[] toArray(final T[] a) { return getDelegate().values().toArray(a); } @Override public boolean add(final V e) { throw newUnmodifiableException(); } @Override public boolean remove(final Object o) { throw newUnmodifiableException(); } @Override public boolean containsAll(final Collection<?> c) { return getDelegate().values().containsAll(c); } @Override public boolean addAll(final Collection<? extends V> c) { throw newUnmodifiableException(); } @Override public boolean retainAll(final Collection<?> c) { throw newUnmodifiableException(); } @Override public boolean removeAll(final Collection<?> c) { throw newUnmodifiableException(); } @Override public void clear() { throw newUnmodifiableException(); } }; public AFastIterableDelegateMap() { refreshFastIterable(); } @Override public V put(final K key, final V value) { final V prev = super.put(key, value); if (prev == null) { addToFastIterable(key, value); } else if (prev != value) { refreshFastIterable(); } return prev; } protected void addToFastIterable(final K key, final V value) { if (fastIterable != null) { fastIterable.add(ImmutableEntry.of(key, value)); } entryArray = null; keyArray = null; valueArray = null; empty = false; size++; } @Override public void clear() { super.clear(); fastIterable = new BufferingIterator<Entry<K, V>>(); entryArray = null; keyArray = null; valueArray = null; empty = true; size = 0; } @Override public V remove(final Object key) { final V removed = super.remove(key); if (removed != null) { refreshFastIterable(); } return removed; } /** * protected so it can be used inside addToFastIterable to refresh instead if desired by overriding */ protected void refreshFastIterable() { fastIterable = null; entryArray = null; keyArray = null; valueArray = null; size = getDelegate().size(); empty = size == 0; } @Override public boolean remove(final Object key, final Object value) { final boolean removed = super.remove(key, value); if (removed) { refreshFastIterable(); } return removed; } @Override public Collection<V> values() { return values; } @SuppressWarnings("unchecked") public V[] asValueArray(final Class<V> valueType) { if (valueArray == null) { final V[] empty = (V[]) Array.newInstance(valueType, size()); valueArray = values.toArray(empty); } return valueArray; } @Override public Set<K> keySet() { return keySet; } @SuppressWarnings("unchecked") public K[] asKeyArray(final Class<K> keyType) { if (keyArray == null) { final K[] empty = (K[]) Array.newInstance(keyType, size()); keyArray = keySet.toArray(empty); } return keyArray; } @Override public Set<Entry<K, V>> entrySet() { return entrySet; } @SuppressWarnings("unchecked") public Entry<K, V>[] asEntryArray() { if (entryArray == null) { final Entry<K, V>[] empty = (Entry<K, V>[]) Array.newInstance(Entry.class, size()); entryArray = entrySet.toArray(empty); } return entryArray; } @Override public boolean isEmpty() { return empty; } @Override public int size() { return size; } private UnsupportedOperationException newUnmodifiableException() { return new UnsupportedOperationException( "Unmodifiable, only size/isEmpty/contains/containsAll/iterator/toArray methods supported"); } }