| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.ignite.internal.util; |
| |
| import java.io.Serializable; |
| import java.util.AbstractMap; |
| import java.util.AbstractSet; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| import org.apache.ignite.internal.util.typedef.F; |
| import org.apache.ignite.internal.util.typedef.internal.A; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * Lean map implementation that keeps up to five entries in its fields. |
| * {@code Null}-keys are not supported. |
| */ |
| public class GridLeanMap<K, V> extends GridSerializableMap<K, V> implements Cloneable { |
| /** */ |
| private static final long serialVersionUID = 0L; |
| |
| /** Implementation used internally. */ |
| private LeanMap<K, V> map; |
| |
| /** |
| * Constructs lean map with initial size of {@code 3}. |
| */ |
| public GridLeanMap() { |
| this(3); |
| } |
| |
| /** |
| * Constructs lean map with initial size. |
| * <p> |
| * If given size is greater than zero then map activates batch mode for <tt>put()</tt> |
| * operations. In this mode map allows to put up to <tt>size</tt> number of key-value |
| * pairs without internal size optimization, i.e. each next <tt>put()</tt> in batch |
| * doesn't switch backing implementation so that initial implementation is used as long |
| * as its capacity allows. |
| * <p> |
| * Note that any removal operation either through iterator or map |
| * itself turns batch mode off and map starts optimizing size after any modification. |
| * |
| * @param size Initial size. |
| */ |
| public GridLeanMap(int size) { |
| assert size >= 0; |
| |
| if (size == 0) |
| map = null; |
| else if (size == 1) |
| map = new Map1<>(); |
| else if (size == 2) |
| map = new Map2<>(); |
| else if (size == 3) |
| map = new Map3<>(); |
| else if (size == 4) |
| map = new Map4<>(); |
| else if (size == 5) |
| map = new Map5<>(); |
| else |
| map = new LeanHashMap<>(IgniteUtils.capacity(size), 0.75f); |
| } |
| |
| /** |
| * Constructs lean map. |
| * |
| * @param m Map to copy entries from. |
| */ |
| public GridLeanMap(Map<K, V> m) { |
| buildFrom(m); |
| } |
| |
| /** |
| * @param m Map to build from. |
| */ |
| private void buildFrom(Map<K, V> m) { |
| Iterator<Entry<K, V>> iter = m.entrySet().iterator(); |
| |
| if (m.isEmpty()) |
| map = null; |
| else if (m.size() == 1) { |
| Entry<K, V> e = iter.next(); |
| |
| map = new Map1<>(e.getKey(), e.getValue()); |
| } |
| else if (m.size() == 2) { |
| Entry<K, V> e1 = iter.next(); |
| Entry<K, V> e2 = iter.next(); |
| |
| map = new Map2<>(e1.getKey(), e1.getValue(), e2.getKey(), e2.getValue()); |
| } |
| else if (m.size() == 3) { |
| Entry<K, V> e1 = iter.next(); |
| Entry<K, V> e2 = iter.next(); |
| Entry<K, V> e3 = iter.next(); |
| |
| map = new Map3<>(e1.getKey(), e1.getValue(), e2.getKey(), e2.getValue(), e3.getKey(), e3.getValue()); |
| } |
| else if (m.size() == 4) { |
| Entry<K, V> e1 = iter.next(); |
| Entry<K, V> e2 = iter.next(); |
| Entry<K, V> e3 = iter.next(); |
| Entry<K, V> e4 = iter.next(); |
| |
| map = new Map4<>(e1.getKey(), e1.getValue(), e2.getKey(), e2.getValue(), e3.getKey(), e3.getValue(), |
| e4.getKey(), e4.getValue()); |
| } |
| else if (m.size() == 5) { |
| Entry<K, V> e1 = iter.next(); |
| Entry<K, V> e2 = iter.next(); |
| Entry<K, V> e3 = iter.next(); |
| Entry<K, V> e4 = iter.next(); |
| Entry<K, V> e5 = iter.next(); |
| |
| map = new Map5<>(e1.getKey(), e1.getValue(), e2.getKey(), e2.getValue(), e3.getKey(), e3.getValue(), |
| e4.getKey(), e4.getValue(), e5.getKey(), e5.getValue()); |
| } |
| else |
| map = new LeanHashMap<>(m); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int size() { |
| return map != null ? map.size() : 0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsKey(Object key) { |
| A.notNull(key, "key"); |
| |
| return map != null && map.containsKey(key); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsValue(Object val) { |
| return map != null && map.containsValue(val); |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V get(Object key) { |
| A.notNull(key, "key"); |
| |
| return map != null ? map.get(key) : null; |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V put(K key, V val) throws NullPointerException { |
| A.notNull(key, "key"); |
| |
| if (map == null) { |
| map = new Map1<>(key, val); |
| |
| return null; |
| } |
| |
| if (!map.isFull() || map.containsKey(key)) |
| return map.put(key, val); |
| |
| int size = map.size(); |
| |
| // Switch implementation. |
| if (size == 1) { |
| Map1<K, V> m = (Map1<K, V>)map; |
| |
| map = new Map2<>(m.k1, m.v1, key, val); |
| } |
| else if (size == 2) { |
| Map2<K, V> m = (Map2<K, V>)map; |
| |
| map = new Map3<>(m.k1, m.v1, m.k2, m.v2, key, val); |
| } |
| else if (size == 3) { |
| Map3<K, V> m = (Map3<K, V>)map; |
| |
| map = new Map4<>(m.k1, m.v1, m.k2, m.v2, m.k3, m.v3, key, val); |
| } |
| else if (size == 4) { |
| Map4<K, V> m = (Map4<K, V>)map; |
| |
| map = new Map5<>(m.k1, m.v1, m.k2, m.v2, m.k3, m.v3, m.k4, m.v4, key, val); |
| } |
| else if (size == 5) { |
| Map<K, V> m = map; |
| |
| map = new LeanHashMap<>(6, 1.0f); |
| |
| map.putAll(m); |
| |
| map.put(key, val); |
| } |
| else |
| map.put(key, val); |
| |
| return null; |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V remove(Object key) { |
| A.notNull(key, "key"); |
| |
| V old; |
| |
| if (map instanceof LeanHashMap) { |
| old = map.remove(key); |
| |
| if (map.size() > 5) |
| return old; |
| else { |
| buildFrom(map); |
| |
| return old; |
| } |
| } |
| else { |
| if (map == null) |
| return null; |
| |
| int size = map.size(); |
| |
| old = map.remove(key); |
| |
| if (map.size() < size) |
| buildFrom(map); |
| |
| return old; |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public void clear() { |
| map = null; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public Set<Entry<K, V>> entrySet() { |
| return new EntrySet(); |
| } |
| |
| /** {@inheritDoc} */ |
| @SuppressWarnings({"CloneDoesntDeclareCloneNotSupportedException"}) |
| @Override protected Object clone() { |
| try { |
| GridLeanMap<K, V> clone = (GridLeanMap<K, V>)super.clone(); |
| |
| clone.buildFrom(this); |
| |
| return clone; |
| } |
| catch (CloneNotSupportedException ignore) { |
| throw new InternalError(); |
| } |
| } |
| |
| /** |
| * Entry set. |
| */ |
| private class EntrySet extends AbstractSet<Entry<K, V>> { |
| /** {@inheritDoc} */ |
| @Override public Iterator<Entry<K, V>> iterator() { |
| return new Iterator<Entry<K, V>>() { |
| /** */ |
| private int idx = -1; |
| |
| /** */ |
| private Iterator<Entry<K, V>> mapIter; |
| |
| /** */ |
| private Entry<K, V> curEnt; |
| |
| /** |
| * @param forceNew If forced to create new instance. |
| * @return Iterator for internal map entry set. |
| */ |
| @SuppressWarnings("IfMayBeConditional") |
| private Iterator<Entry<K, V>> getMapIterator(boolean forceNew) { |
| if (mapIter == null || forceNew) { |
| if (map != null) |
| mapIter = map.entrySet().iterator(); |
| else { |
| mapIter = new Iterator<Entry<K, V>>() { |
| @Override public boolean hasNext() { |
| return false; |
| } |
| |
| @Override public Entry<K, V> next() { |
| throw new NoSuchElementException(); |
| } |
| |
| @Override public void remove() { |
| throw new IllegalStateException(); |
| } |
| }; |
| } |
| } |
| |
| return mapIter; |
| } |
| |
| @Override public boolean hasNext() { |
| return map != null && getMapIterator(false).hasNext(); |
| } |
| |
| @Override public Entry<K, V> next() { |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| |
| idx++; |
| |
| return curEnt = getMapIterator(false).next(); |
| } |
| |
| @Override public void remove() { |
| if (curEnt == null) |
| throw new IllegalStateException(); |
| |
| GridLeanMap.this.remove(curEnt.getKey()); |
| |
| curEnt = null; |
| |
| mapIter = getMapIterator(true); |
| |
| for (int i = 0; i < idx && mapIter.hasNext(); i++) |
| mapIter.next(); |
| |
| idx--; |
| } |
| }; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int size() { |
| return GridLeanMap.this.size(); |
| } |
| } |
| |
| /** |
| * |
| */ |
| private interface LeanMap<K, V> extends Map<K, V> { |
| /** |
| * @return {@code True} if map is full. |
| */ |
| public boolean isFull(); |
| } |
| |
| /** |
| * Map for single entry. |
| */ |
| private static class Map1<K, V> extends AbstractMap<K, V> implements LeanMap<K, V>, Serializable { |
| /** */ |
| private static final long serialVersionUID = 0L; |
| |
| /** */ |
| protected K k1; |
| |
| /** */ |
| protected V v1; |
| |
| /** |
| * Constructs map. |
| */ |
| Map1() { |
| // No-op. |
| } |
| |
| /** |
| * Constructs map. |
| * |
| * @param k1 Key. |
| * @param v1 Value. |
| */ |
| Map1(K k1, V v1) { |
| this.k1 = k1; |
| this.v1 = v1; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isFull() { |
| return size() == 1; |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V remove(Object key) { |
| V res = null; |
| |
| if (F.eq(key, k1)) { |
| res = v1; |
| |
| v1 = null; |
| k1 = null; |
| } |
| |
| return res; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int size() { |
| return k1 != null ? 1 : 0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isEmpty() { |
| return size() == 0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsKey(Object key) { |
| return k1 != null && F.eq(key, k1); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsValue(Object val) { |
| return k1 != null && F.eq(val, v1); |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V get(Object key) { |
| return k1 != null && F.eq(key, k1) ? v1 : null; |
| } |
| |
| /** |
| * Puts key-value pair into map only if given key is already contained in the map |
| * or there are free slots. |
| * Note that this implementation of {@link Map#put(Object, Object)} does not match |
| * general contract of {@link Map} interface and serves only for internal purposes. |
| * |
| * @param key Key. |
| * @param val Value. |
| * @return Previous value associated with given key. |
| */ |
| @Nullable @Override public V put(K key, V val) { |
| V oldVal = get(key); |
| |
| if (k1 == null || F.eq(k1, key)) { |
| k1 = key; |
| v1 = val; |
| } |
| |
| return oldVal; |
| } |
| |
| /** |
| * @param key Key. |
| * @param val Value. |
| * @return New entry. |
| */ |
| protected Entry<K, V> e(K key, V val) { |
| return new SimpleImmutableEntry<>(key, val); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public Set<Entry<K, V>> entrySet() { |
| return new AbstractSet<Entry<K, V>>() { |
| @Override public Iterator<Entry<K, V>> iterator() { |
| return new Iterator<Entry<K, V>>() { |
| private int idx; |
| |
| @Override public boolean hasNext() { |
| return idx == 0 && k1 != null; |
| } |
| |
| @Override public Entry<K, V> next() { |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| |
| idx = 1; |
| |
| return e(k1, v1); |
| } |
| |
| @Override public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| @Override public int size() { |
| return Map1.this.size(); |
| } |
| }; |
| } |
| } |
| |
| /** |
| * Map for two entries. |
| */ |
| private static class Map2<K, V> extends Map1<K, V> { |
| /** */ |
| private static final long serialVersionUID = 0L; |
| |
| /** */ |
| protected K k2; |
| |
| /** */ |
| protected V v2; |
| |
| /** |
| * Constructs map. |
| */ |
| Map2() { |
| // No-op. |
| } |
| |
| /** |
| * Constructs map. |
| * |
| * @param k1 Key1. |
| * @param v1 Value1. |
| * @param k2 Key2. |
| * @param v2 Value2. |
| */ |
| Map2(K k1, V v1, K k2, V v2) { |
| super(k1, v1); |
| |
| this.k2 = k2; |
| this.v2 = v2; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isFull() { |
| return size() == 2; |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V remove(Object key) { |
| if (F.eq(key, k2)) { |
| V res = v2; |
| |
| v2 = null; |
| k2 = null; |
| |
| return res; |
| } |
| |
| return super.remove(key); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int size() { |
| return super.size() + (k2 != null ? 1 : 0); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsKey(Object k) { |
| return super.containsKey(k) || (k2 != null && F.eq(k, k2)); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsValue(Object v) { |
| return super.containsValue(v) || (k2 != null && F.eq(v, v2)); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public V get(Object k) { |
| V v = super.get(k); |
| |
| return v != null ? v : (k2 != null && F.eq(k, k2)) ? v2 : null; |
| } |
| |
| /** |
| * Puts key-value pair into map only if given key is already contained in the map |
| * or there are free slots. |
| * Note that this implementation of {@link Map#put(Object, Object)} does not match |
| * general contract of {@link Map} interface and serves only for internal purposes. |
| * |
| * @param key Key. |
| * @param val Value. |
| * @return Previous value associated with given key. |
| */ |
| @Nullable @Override public V put(K key, V val) throws NullPointerException { |
| V oldVal = get(key); |
| |
| if (k1 == null || F.eq(k1, key)) { |
| k1 = key; |
| v1 = val; |
| } |
| else if (k2 == null || F.eq(k2, key)) { |
| k2 = key; |
| v2 = val; |
| } |
| |
| return oldVal; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public Set<Entry<K, V>> entrySet() { |
| return new AbstractSet<Entry<K, V>>() { |
| @Override public Iterator<Entry<K, V>> iterator() { |
| return new Iterator<Entry<K, V>>() { |
| private int idx; |
| |
| private Entry<K, V> next; |
| |
| { |
| if (k1 != null) { |
| idx = 1; |
| next = e(k1, v1); |
| } |
| else if (k2 != null) { |
| idx = 2; |
| next = e(k2, v2); |
| } |
| } |
| |
| @Override public boolean hasNext() { |
| return next != null; |
| } |
| |
| @Override public Entry<K, V> next() { |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| |
| Entry<K, V> old = next; |
| |
| next = null; |
| |
| if (idx == 1 && k2 != null) { |
| idx = 2; |
| next = e(k2, v2); |
| } |
| |
| return old; |
| } |
| |
| @Override public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| @Override public int size() { |
| return Map2.this.size(); |
| } |
| }; |
| } |
| } |
| |
| /** |
| * Map for three entries. |
| */ |
| private static class Map3<K, V> extends Map2<K, V> { |
| /** */ |
| private static final long serialVersionUID = 0L; |
| |
| /** */ |
| protected K k3; |
| |
| /** */ |
| protected V v3; |
| |
| /** |
| * Constructs map. |
| */ |
| Map3() { |
| // No-op. |
| } |
| |
| /** |
| * Constructs map. |
| * |
| * @param k1 Key1. |
| * @param v1 Value1. |
| * @param k2 Key2. |
| * @param v2 Value2. |
| * @param k3 Key3. |
| * @param v3 Value3. |
| */ |
| Map3(K k1, V v1, K k2, V v2, K k3, V v3) { |
| super(k1, v1, k2, v2); |
| |
| this.k3 = k3; |
| this.v3 = v3; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isFull() { |
| return size() == 3; |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V remove(Object key) { |
| if (F.eq(key, k3)) { |
| V res = v3; |
| |
| v3 = null; |
| k3 = null; |
| |
| return res; |
| } |
| |
| return super.remove(key); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int size() { |
| return super.size() + (k3 != null ? 1 : 0); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsKey(Object k) { |
| return super.containsKey(k) || (k3 != null && F.eq(k, k3)); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsValue(Object v) { |
| return super.containsValue(v) || (k3 != null && F.eq(v, v3)); |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V get(Object k) { |
| V v = super.get(k); |
| |
| return v != null ? v : (k3 != null && F.eq(k, k3)) ? v3 : null; |
| } |
| |
| /** |
| * Puts key-value pair into map only if given key is already contained in the map |
| * or there are free slots. |
| * Note that this implementation of {@link Map#put(Object, Object)} does not match |
| * general contract of {@link Map} interface and serves only for internal purposes. |
| * |
| * @param key Key. |
| * @param val Value. |
| * @return Previous value associated with given key. |
| */ |
| @Nullable @Override public V put(K key, V val) throws NullPointerException { |
| V oldVal = get(key); |
| |
| if (k1 == null || F.eq(k1, key)) { |
| k1 = key; |
| v1 = val; |
| } |
| else if (k2 == null || F.eq(k2, key)) { |
| k2 = key; |
| v2 = val; |
| } |
| else if (k3 == null || F.eq(k3, key)) { |
| k3 = key; |
| v3 = val; |
| } |
| |
| return oldVal; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public Set<Entry<K, V>> entrySet() { |
| return new AbstractSet<Entry<K, V>>() { |
| @Override public Iterator<Entry<K, V>> iterator() { |
| return new Iterator<Entry<K, V>>() { |
| private int idx; |
| |
| private Entry<K, V> next; |
| |
| { |
| if (k1 != null) { |
| idx = 1; |
| next = e(k1, v1); |
| } |
| else if (k2 != null) { |
| idx = 2; |
| next = e(k2, v2); |
| } |
| else if (k3 != null) { |
| idx = 3; |
| next = e(k3, v3); |
| } |
| } |
| |
| @Override public boolean hasNext() { |
| return next != null; |
| } |
| |
| @SuppressWarnings("fallthrough") |
| @Override public Entry<K, V> next() { |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| |
| Entry<K, V> old = next; |
| |
| next = null; |
| |
| switch (idx) { |
| case 1: |
| if (k2 != null) { |
| idx = 2; |
| next = e(k2, v2); |
| |
| break; |
| } |
| |
| case 2: |
| if (k3 != null) { |
| idx = 3; |
| next = e(k3, v3); |
| |
| break; |
| } |
| } |
| |
| return old; |
| } |
| |
| @Override public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| @Override public int size() { |
| return Map3.this.size(); |
| } |
| }; |
| } |
| } |
| |
| /** |
| * Map for four entries. |
| */ |
| private static class Map4<K, V> extends Map3<K, V> { |
| /** */ |
| private static final long serialVersionUID = 0L; |
| |
| /** */ |
| protected K k4; |
| |
| /** */ |
| protected V v4; |
| |
| /** |
| * Constructs map. |
| */ |
| Map4() { |
| // No-op. |
| } |
| |
| /** |
| * Constructs map. |
| * |
| * @param k1 Key1. |
| * @param v1 Value1. |
| * @param k2 Key2. |
| * @param v2 Value2. |
| * @param k3 Key3. |
| * @param v3 Value3. |
| * @param k4 Key4. |
| * @param v4 Value4. |
| */ |
| Map4(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) { |
| super(k1, v1, k2, v2, k3, v3); |
| |
| this.k4 = k4; |
| this.v4 = v4; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isFull() { |
| return size() == 4; |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V remove(Object key) { |
| if (F.eq(key, k4)) { |
| V res = v4; |
| |
| v4 = null; |
| k4 = null; |
| |
| return res; |
| } |
| |
| return super.remove(key); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int size() { |
| return super.size() + (k4 != null ? 1 : 0); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsKey(Object k) { |
| return super.containsKey(k) || (k4 != null && F.eq(k, k4)); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsValue(Object v) { |
| return super.containsValue(v) || (k4 != null && F.eq(v, v4)); |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V get(Object k) { |
| V v = super.get(k); |
| |
| return v != null ? v : (k4 != null && F.eq(k, k4)) ? v4 : null; |
| } |
| |
| /** |
| * Puts key-value pair into map only if given key is already contained in the map |
| * or there are free slots. |
| * Note that this implementation of {@link Map#put(Object, Object)} does not match |
| * general contract of {@link Map} interface and serves only for internal purposes. |
| * |
| * @param key Key. |
| * @param val Value. |
| * @return Previous value associated with given key. |
| */ |
| @Nullable @Override public V put(K key, V val) throws NullPointerException { |
| V oldVal = get(key); |
| |
| if (k1 == null || F.eq(k1, key)) { |
| k1 = key; |
| v1 = val; |
| } |
| else if (k2 == null || F.eq(k2, key)) { |
| k2 = key; |
| v2 = val; |
| } |
| else if (k3 == null || F.eq(k3, key)) { |
| k3 = key; |
| v3 = val; |
| } |
| else if (k4 == null || F.eq(k4, key)) { |
| k4 = key; |
| v4 = val; |
| } |
| |
| return oldVal; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public Set<Entry<K, V>> entrySet() { |
| return new AbstractSet<Entry<K, V>>() { |
| @Override public Iterator<Entry<K, V>> iterator() { |
| return new Iterator<Entry<K, V>>() { |
| private int idx; |
| |
| private Entry<K, V> next; |
| |
| { |
| if (k1 != null) { |
| idx = 1; |
| next = e(k1, v1); |
| } |
| else if (k2 != null) { |
| idx = 2; |
| next = e(k2, v2); |
| } |
| else if (k3 != null) { |
| idx = 3; |
| next = e(k3, v3); |
| } |
| else if (k4 != null) { |
| idx = 4; |
| next = e(k4, v4); |
| } |
| } |
| |
| @Override public boolean hasNext() { |
| return next != null; |
| } |
| |
| @SuppressWarnings("fallthrough") |
| @Override public Entry<K, V> next() { |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| |
| Entry<K, V> old = next; |
| |
| next = null; |
| |
| switch (idx) { |
| case 1: |
| if (k2 != null) { |
| idx = 2; |
| next = e(k2, v2); |
| |
| break; |
| } |
| |
| case 2: |
| if (k3 != null) { |
| idx = 3; |
| next = e(k3, v3); |
| |
| break; |
| } |
| |
| case 3: |
| if (k4 != null) { |
| idx = 4; |
| next = e(k4, v4); |
| |
| break; |
| } |
| } |
| |
| return old; |
| } |
| |
| @Override public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| @Override public int size() { |
| return Map4.this.size(); |
| } |
| }; |
| } |
| } |
| |
| /** |
| * Map for five entries. |
| */ |
| private static class Map5<K, V> extends Map4<K, V> { |
| /** */ |
| private static final long serialVersionUID = 0L; |
| |
| /** */ |
| private K k5; |
| |
| /** */ |
| private V v5; |
| |
| /** |
| * Constructs map. |
| */ |
| Map5() { |
| // No-op. |
| } |
| |
| /** |
| * Constructs map. |
| * |
| * @param k1 Key1. |
| * @param v1 Value1. |
| * @param k2 Key2. |
| * @param v2 Value2. |
| * @param k3 Key3. |
| * @param v3 Value3. |
| * @param k4 Key4. |
| * @param v4 Value4. |
| * @param k5 Key5. |
| * @param v5 Value5. |
| */ |
| Map5(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) { |
| super(k1, v1, k2, v2, k3, v3, k4, v4); |
| |
| this.k5 = k5; |
| this.v5 = v5; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isFull() { |
| return size() == 5; |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V remove(Object key) { |
| if (F.eq(key, k5)) { |
| V res = v5; |
| |
| v5 = null; |
| k5 = null; |
| |
| return res; |
| } |
| |
| return super.remove(key); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public int size() { |
| return super.size() + (k5 != null ? 1 : 0); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsKey(Object k) { |
| return super.containsKey(k) || (k5 != null && F.eq(k, k5)); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean containsValue(Object v) { |
| return super.containsValue(v) || (k5 != null && F.eq(v, v5)); |
| } |
| |
| /** {@inheritDoc} */ |
| @Nullable @Override public V get(Object k) { |
| V v = super.get(k); |
| |
| return v != null ? v : (k5 != null && F.eq(k, k5)) ? v5 : null; |
| } |
| |
| /** |
| * Puts key-value pair into map only if given key is already contained in the map |
| * or there are free slots. |
| * Note that this implementation of {@link Map#put(Object, Object)} does not match |
| * general contract of {@link Map} interface and serves only for internal purposes. |
| * |
| * @param key Key. |
| * @param val Value. |
| * @return Previous value associated with given key. |
| */ |
| @Nullable @Override public V put(K key, V val) throws NullPointerException { |
| V oldVal = get(key); |
| |
| if (k1 == null || F.eq(k1, key)) { |
| k1 = key; |
| v1 = val; |
| } |
| else if (k2 == null || F.eq(k2, key)) { |
| k2 = key; |
| v2 = val; |
| } |
| else if (k3 == null || F.eq(k3, key)) { |
| k3 = key; |
| v3 = val; |
| } |
| else if (k4 == null || F.eq(k4, key)) { |
| k4 = key; |
| v4 = val; |
| } |
| else if (k5 == null || F.eq(k5, key)) { |
| k5 = key; |
| v5 = val; |
| } |
| |
| return oldVal; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public Set<Entry<K, V>> entrySet() { |
| return new AbstractSet<Entry<K, V>>() { |
| @Override public Iterator<Entry<K, V>> iterator() { |
| return new Iterator<Entry<K, V>>() { |
| private int idx; |
| |
| private Entry<K, V> next; |
| |
| { |
| if (k1 != null) { |
| idx = 1; |
| next = e(k1, v1); |
| } |
| else if (k2 != null) { |
| idx = 2; |
| next = e(k2, v2); |
| } |
| else if (k3 != null) { |
| idx = 3; |
| next = e(k3, v3); |
| } |
| else if (k4 != null) { |
| idx = 4; |
| next = e(k4, v4); |
| } |
| else if (k5 != null) { |
| idx = 5; |
| next = e(k5, v5); |
| } |
| } |
| |
| @Override public boolean hasNext() { |
| return next != null; |
| } |
| |
| @SuppressWarnings("fallthrough") |
| @Override public Entry<K, V> next() { |
| if (!hasNext()) |
| throw new NoSuchElementException(); |
| |
| Entry<K, V> old = next; |
| |
| next = null; |
| |
| switch (idx) { |
| case 1: |
| if (k2 != null) { |
| idx = 2; |
| next = e(k2, v2); |
| |
| break; |
| } |
| |
| case 2: |
| if (k3 != null) { |
| idx = 3; |
| next = e(k3, v3); |
| |
| break; |
| } |
| |
| case 3: |
| if (k4 != null) { |
| idx = 4; |
| next = e(k4, v4); |
| |
| break; |
| } |
| |
| case 4: |
| if (k5 != null) { |
| idx = 5; |
| next = e(k5, v5); |
| |
| break; |
| } |
| } |
| |
| return old; |
| } |
| |
| @Override public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| }; |
| } |
| |
| @Override public int size() { |
| return Map5.this.size(); |
| } |
| }; |
| } |
| } |
| |
| /** |
| * |
| */ |
| private static class LeanHashMap<K, V> extends HashMap<K, V> implements LeanMap<K, V> { |
| /** */ |
| private static final long serialVersionUID = 0L; |
| |
| /** |
| * @param initCap Capacity. |
| * @param loadFactor Load factor. |
| */ |
| private LeanHashMap(int initCap, float loadFactor) { |
| super(initCap, loadFactor); |
| } |
| |
| /** |
| * @param m Map. |
| */ |
| private LeanHashMap(Map<? extends K, ? extends V> m) { |
| super(m); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override public boolean isFull() { |
| return false; |
| } |
| } |
| } |