|
|
@@ -1,366 +1,365 @@
|
|
|
-/*
|
|
|
- * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
- * @copyright Ranides Atterwim
|
|
|
- * @license WTFPL
|
|
|
- * @url http://ranides.net/projects/assira
|
|
|
- */
|
|
|
-package net.ranides.assira.collection.map;
|
|
|
-
|
|
|
-import java.util.AbstractList;
|
|
|
-import java.util.AbstractMap;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Collection;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.Iterator;
|
|
|
-import java.util.List;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.RandomAccess;
|
|
|
-import java.util.Set;
|
|
|
-import net.ranides.assira.generic.ValueUtils;
|
|
|
-import net.ranides.assira.text.StrBuilder;
|
|
|
-
|
|
|
-/**
|
|
|
- * Usuwanie jest realizowane w czasie O(N), reszta idzie bardzo ładnie.
|
|
|
- * <p>
|
|
|
- * Wcześniej mieliśmy implementację która się opierała naiwnie na apache.commons.
|
|
|
- * Naiwnie - bo oni także mieli złe złożoności przy usuwaniu elementów, a nawet
|
|
|
- * dużo dużo częściej (tak samo używali ArrayList do przechowywania kolejności
|
|
|
- * kluczy).
|
|
|
- * </p>
|
|
|
- * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
- * @todo (migration) assira
|
|
|
- */
|
|
|
-public class RandomAccessMap<K,V> implements ListableMap<K, V> {
|
|
|
-
|
|
|
- private final List<K> keys;
|
|
|
- private final Map<K, V> map;
|
|
|
-
|
|
|
- public RandomAccessMap() {
|
|
|
- keys = new ArrayList<>();
|
|
|
- map = new HashMap<>();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public List<V> asListAdapter() {
|
|
|
- return new ValueListAdapter();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public List<K> keysList() {
|
|
|
- return new KeyListAdapter();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public List<Entry<K, V>> entryList() {
|
|
|
- return new EntryListAdapter();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V at(int index) {
|
|
|
- return map.get(keys.get(index));
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int size() {
|
|
|
- return keys.size();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean isEmpty() {
|
|
|
- return keys.isEmpty();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean containsKey(Object key) {
|
|
|
- return map.containsKey(key);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- @SuppressWarnings("element-type-mismatch")
|
|
|
- public boolean containsValue(Object value) {
|
|
|
- return map.containsValue(value);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V get(Object key) {
|
|
|
- return map.get(key);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V put(K key, V value) {
|
|
|
- if( map.containsKey(key) ) {
|
|
|
- // just change value
|
|
|
- return map.put(key, value);
|
|
|
- } else {
|
|
|
- // add new value
|
|
|
- keys.add(key);
|
|
|
- map.put(key, value);
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- @SuppressWarnings("element-type-mismatch")
|
|
|
- public V remove(Object key) {
|
|
|
- if( !map.containsKey(key) ) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- // remove from keys: O(N)
|
|
|
- keys.remove(key);
|
|
|
- return map.remove(key);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void putAll(Map<? extends K, ? extends V> values) {
|
|
|
- for(Map.Entry<? extends K, ? extends V> entry : values.entrySet()) {
|
|
|
- put(entry.getKey(), entry.getValue());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void clear() {
|
|
|
- keys.clear();
|
|
|
- map.clear();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Set<K> keySet() {
|
|
|
- return new KeyListAdapter();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Collection<V> values() {
|
|
|
- return asListAdapter();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Set<Entry<K, V>> entrySet() {
|
|
|
- return new EntryListAdapter();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Iterator<V> iterator() {
|
|
|
- return asListAdapter().iterator();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public String toString() {
|
|
|
- StrBuilder builder = new StrBuilder();
|
|
|
- builder.append('{');
|
|
|
- builder.list(", ");
|
|
|
- for(K key : keys) {
|
|
|
- builder.item().append(key).append('=').append(map.get(key));
|
|
|
- }
|
|
|
- builder.append('}');
|
|
|
- return builder.toString();
|
|
|
- }
|
|
|
-
|
|
|
- private class ValueListAdapter extends AbstractList<V> implements RandomAccess {
|
|
|
-
|
|
|
- @Override
|
|
|
- public int size() {
|
|
|
- return map.size();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void clear() {
|
|
|
- RandomAccessMap.this.clear();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V get(int index) {
|
|
|
- return RandomAccessMap.this.at(index);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V set(int index, V element) {
|
|
|
- return map.put(keys.get(index), element);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V remove(int index) {
|
|
|
- // remove from keys: O(N)
|
|
|
- return map.remove(keys.remove(index));
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- @SuppressWarnings("element-type-mismatch")
|
|
|
- public boolean contains(Object value) {
|
|
|
- return map.containsValue(value);
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- private class KeyListAdapter extends AbstractList<K> implements Set<K>, RandomAccess {
|
|
|
-
|
|
|
- @Override
|
|
|
- public int size() {
|
|
|
- return keys.size();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- @SuppressWarnings("element-type-mismatch")
|
|
|
- public boolean remove(Object key) {
|
|
|
- return null != RandomAccessMap.this.remove(key);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public K remove(int index) {
|
|
|
- // remove from keys: O(N)
|
|
|
- K key = keys.remove(index);
|
|
|
- map.remove(key);
|
|
|
- return key;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void clear() {
|
|
|
- RandomAccessMap.this.clear();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public K get(int index) {
|
|
|
- return keys.get(index);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public K set(int index, K element) {
|
|
|
- if( map.containsKey(element) ) {
|
|
|
- throw new IllegalArgumentException("key conflict. Key already exists");
|
|
|
- }
|
|
|
- K prev = keys.get(index);
|
|
|
- map.put(element, map.remove(prev));
|
|
|
- keys.set(index, element);
|
|
|
- return prev;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- private class EntryListAdapter extends AbstractList<Entry<K,V>> implements Set<Entry<K,V>>, RandomAccess {
|
|
|
-
|
|
|
- @Override
|
|
|
- public int size() {
|
|
|
- return keys.size();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean contains(Object object) {
|
|
|
- if(!(object instanceof Entry)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- Entry<?,?> element = (Entry<?,?>)object;
|
|
|
- Object key = element.getKey();
|
|
|
- Object val = element.getValue();
|
|
|
- V found = map.get(key);
|
|
|
- if(null == found) {
|
|
|
- return (null == val) && map.containsKey(key);
|
|
|
- } else {
|
|
|
- return found.equals(val);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean add(Entry<K, V> entry) {
|
|
|
- RandomAccessMap.this.put(entry.getKey(), entry.getValue());
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- @SuppressWarnings("element-type-mismatch")
|
|
|
- public boolean remove(Object object) {
|
|
|
- if(!(object instanceof Entry)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- Entry<?,?> entry = (Entry<?,?>)object;
|
|
|
- Object key = entry.getKey();
|
|
|
- if ( !ValueUtils.equals(entry.getValue(), map.get(key)) ) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- // remove from keys: O(N)
|
|
|
- keys.remove(key);
|
|
|
- map.remove(key);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Entry<K, V> remove(int index) {
|
|
|
- K key = keys.get(index);
|
|
|
- V val = map.get(key);
|
|
|
- // remove from keys: O(N)
|
|
|
- keys.remove(key);
|
|
|
- map.remove(key);
|
|
|
- return new AbstractMap.SimpleImmutableEntry<>(key, val);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void clear() {
|
|
|
- RandomAccessMap.this.clear();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Entry<K, V> get(int index) {
|
|
|
- return new EntryAdapter(RandomAccessMap.this, index);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Entry<K, V> set(int index, Entry<K, V> element) {
|
|
|
- K pkey = keys.get(index);
|
|
|
- V pval = map.get(pkey);
|
|
|
-
|
|
|
- if( !pkey.equals(element.getKey()) && map.containsKey(element.getKey()) ) {
|
|
|
- throw new IllegalArgumentException("key conflict. Key already exists");
|
|
|
- }
|
|
|
-
|
|
|
- map.remove(keys.get(index));
|
|
|
- map.put(element.getKey(), element.getValue());
|
|
|
- keys.set(index, element.getKey());
|
|
|
-
|
|
|
- return new AbstractMap.SimpleImmutableEntry(pkey, pval);
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- private static class EntryAdapter<K,V> implements Entry<K,V> {
|
|
|
-
|
|
|
- private final RandomAccessMap<K,V> parent;
|
|
|
- private final int index;
|
|
|
-
|
|
|
- public EntryAdapter(RandomAccessMap<K,V> parent, int index) {
|
|
|
- this.parent = parent;
|
|
|
- this.index = index;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public K getKey() {
|
|
|
- return parent.keys.get(index);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V getValue() {
|
|
|
- return parent.map.get(parent.keys.get(index));
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public V setValue(V value) {
|
|
|
- return parent.map.put(parent.keys.get(index), value);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int hashCode() {
|
|
|
- return this.index;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean equals(Object object) {
|
|
|
- if(object instanceof EntryAdapter<?,?>) {
|
|
|
- EntryAdapter<?,?> entry = (EntryAdapter<?,?>)object;
|
|
|
- return entry.parent==parent && entry.index == index;
|
|
|
- }
|
|
|
- if(object instanceof Entry) {
|
|
|
- final Entry<?,?> entry = (Entry<?,?>)object;
|
|
|
- return ValueUtils.equals(entry.getKey(), getKey()) && ValueUtils.equals(entry.getValue(), getValue());
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-}
|
|
|
+/*
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ * @copyright Ranides Atterwim
|
|
|
+ * @license WTFPL
|
|
|
+ * @url http://ranides.net/projects/assira
|
|
|
+ */
|
|
|
+package net.ranides.assira.collection.map;
|
|
|
+
|
|
|
+import java.util.AbstractList;
|
|
|
+import java.util.AbstractMap;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collection;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.Iterator;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.RandomAccess;
|
|
|
+import java.util.Set;
|
|
|
+import net.ranides.assira.generic.ValueUtils;
|
|
|
+import net.ranides.assira.text.StrBuilder;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Usuwanie jest realizowane w czasie O(N), reszta idzie bardzo ładnie.
|
|
|
+ * <p>
|
|
|
+ * Wcześniej mieliśmy implementację która się opierała naiwnie na apache.commons.
|
|
|
+ * Naiwnie - bo oni także mieli złe złożoności przy usuwaniu elementów, a nawet
|
|
|
+ * dużo dużo częściej (tak samo używali ArrayList do przechowywania kolejności
|
|
|
+ * kluczy).
|
|
|
+ * </p>
|
|
|
+ * @author Ranides Atterwim <ranides@gmail.com>
|
|
|
+ */
|
|
|
+public class RandomAccessMap<K,V> implements ListableMap<K, V> {
|
|
|
+
|
|
|
+ private final List<K> keys;
|
|
|
+ private final Map<K, V> map;
|
|
|
+
|
|
|
+ public RandomAccessMap() {
|
|
|
+ keys = new ArrayList<>();
|
|
|
+ map = new HashMap<>();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<V> asListAdapter() {
|
|
|
+ return new ValueListAdapter();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<K> keysList() {
|
|
|
+ return new KeyListAdapter();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<Entry<K, V>> entryList() {
|
|
|
+ return new EntryListAdapter();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public V at(int index) {
|
|
|
+ return map.get(keys.get(index));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return keys.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean isEmpty() {
|
|
|
+ return keys.isEmpty();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean containsKey(Object key) {
|
|
|
+ return map.containsKey(key);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @SuppressWarnings("element-type-mismatch")
|
|
|
+ public boolean containsValue(Object value) {
|
|
|
+ return map.containsValue(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public V get(Object key) {
|
|
|
+ return map.get(key);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public V put(K key, V value) {
|
|
|
+ if( map.containsKey(key) ) {
|
|
|
+ // just change value
|
|
|
+ return map.put(key, value);
|
|
|
+ } else {
|
|
|
+ // add new value
|
|
|
+ keys.add(key);
|
|
|
+ map.put(key, value);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @SuppressWarnings("element-type-mismatch")
|
|
|
+ public V remove(Object key) {
|
|
|
+ if( !map.containsKey(key) ) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // remove from keys: O(N)
|
|
|
+ keys.remove(key);
|
|
|
+ return map.remove(key);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void putAll(Map<? extends K, ? extends V> values) {
|
|
|
+ for(Map.Entry<? extends K, ? extends V> entry : values.entrySet()) {
|
|
|
+ put(entry.getKey(), entry.getValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void clear() {
|
|
|
+ keys.clear();
|
|
|
+ map.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Set<K> keySet() {
|
|
|
+ return new KeyListAdapter();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Collection<V> values() {
|
|
|
+ return asListAdapter();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Set<Entry<K, V>> entrySet() {
|
|
|
+ return new EntryListAdapter();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Iterator<V> iterator() {
|
|
|
+ return asListAdapter().iterator();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String toString() {
|
|
|
+ StrBuilder builder = new StrBuilder();
|
|
|
+ builder.append('{');
|
|
|
+ builder.list(", ");
|
|
|
+ for(K key : keys) {
|
|
|
+ builder.item().append(key).append('=').append(map.get(key));
|
|
|
+ }
|
|
|
+ builder.append('}');
|
|
|
+ return builder.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ private class ValueListAdapter extends AbstractList<V> implements RandomAccess {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return map.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void clear() {
|
|
|
+ RandomAccessMap.this.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public V get(int index) {
|
|
|
+ return RandomAccessMap.this.at(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public V set(int index, V element) {
|
|
|
+ return map.put(keys.get(index), element);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public V remove(int index) {
|
|
|
+ // remove from keys: O(N)
|
|
|
+ return map.remove(keys.remove(index));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @SuppressWarnings("element-type-mismatch")
|
|
|
+ public boolean contains(Object value) {
|
|
|
+ return map.containsValue(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private class KeyListAdapter extends AbstractList<K> implements Set<K>, RandomAccess {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return keys.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @SuppressWarnings("element-type-mismatch")
|
|
|
+ public boolean remove(Object key) {
|
|
|
+ return null != RandomAccessMap.this.remove(key);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public K remove(int index) {
|
|
|
+ // remove from keys: O(N)
|
|
|
+ K key = keys.remove(index);
|
|
|
+ map.remove(key);
|
|
|
+ return key;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void clear() {
|
|
|
+ RandomAccessMap.this.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public K get(int index) {
|
|
|
+ return keys.get(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public K set(int index, K element) {
|
|
|
+ if( map.containsKey(element) ) {
|
|
|
+ throw new IllegalArgumentException("key conflict. Key already exists");
|
|
|
+ }
|
|
|
+ K prev = keys.get(index);
|
|
|
+ map.put(element, map.remove(prev));
|
|
|
+ keys.set(index, element);
|
|
|
+ return prev;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private class EntryListAdapter extends AbstractList<Entry<K,V>> implements Set<Entry<K,V>>, RandomAccess {
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int size() {
|
|
|
+ return keys.size();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean contains(Object object) {
|
|
|
+ if(!(object instanceof Entry)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Entry<?,?> element = (Entry<?,?>)object;
|
|
|
+ Object key = element.getKey();
|
|
|
+ Object val = element.getValue();
|
|
|
+ V found = map.get(key);
|
|
|
+ if(null == found) {
|
|
|
+ return (null == val) && map.containsKey(key);
|
|
|
+ } else {
|
|
|
+ return found.equals(val);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean add(Entry<K, V> entry) {
|
|
|
+ RandomAccessMap.this.put(entry.getKey(), entry.getValue());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @SuppressWarnings("element-type-mismatch")
|
|
|
+ public boolean remove(Object object) {
|
|
|
+ if(!(object instanceof Entry)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ Entry<?,?> entry = (Entry<?,?>)object;
|
|
|
+ Object key = entry.getKey();
|
|
|
+ if ( !ValueUtils.equals(entry.getValue(), map.get(key)) ) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // remove from keys: O(N)
|
|
|
+ keys.remove(key);
|
|
|
+ map.remove(key);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Entry<K, V> remove(int index) {
|
|
|
+ K key = keys.get(index);
|
|
|
+ V val = map.get(key);
|
|
|
+ // remove from keys: O(N)
|
|
|
+ keys.remove(key);
|
|
|
+ map.remove(key);
|
|
|
+ return new AbstractMap.SimpleImmutableEntry<>(key, val);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void clear() {
|
|
|
+ RandomAccessMap.this.clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Entry<K, V> get(int index) {
|
|
|
+ return new EntryAdapter(RandomAccessMap.this, index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Entry<K, V> set(int index, Entry<K, V> element) {
|
|
|
+ K pkey = keys.get(index);
|
|
|
+ V pval = map.get(pkey);
|
|
|
+
|
|
|
+ if( !pkey.equals(element.getKey()) && map.containsKey(element.getKey()) ) {
|
|
|
+ throw new IllegalArgumentException("key conflict. Key already exists");
|
|
|
+ }
|
|
|
+
|
|
|
+ map.remove(keys.get(index));
|
|
|
+ map.put(element.getKey(), element.getValue());
|
|
|
+ keys.set(index, element.getKey());
|
|
|
+
|
|
|
+ return new AbstractMap.SimpleImmutableEntry(pkey, pval);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static class EntryAdapter<K,V> implements Entry<K,V> {
|
|
|
+
|
|
|
+ private final RandomAccessMap<K,V> parent;
|
|
|
+ private final int index;
|
|
|
+
|
|
|
+ public EntryAdapter(RandomAccessMap<K,V> parent, int index) {
|
|
|
+ this.parent = parent;
|
|
|
+ this.index = index;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public K getKey() {
|
|
|
+ return parent.keys.get(index);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public V getValue() {
|
|
|
+ return parent.map.get(parent.keys.get(index));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public V setValue(V value) {
|
|
|
+ return parent.map.put(parent.keys.get(index), value);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public int hashCode() {
|
|
|
+ return this.index;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean equals(Object object) {
|
|
|
+ if(object instanceof EntryAdapter<?,?>) {
|
|
|
+ EntryAdapter<?,?> entry = (EntryAdapter<?,?>)object;
|
|
|
+ return entry.parent==parent && entry.index == index;
|
|
|
+ }
|
|
|
+ if(object instanceof Entry) {
|
|
|
+ final Entry<?,?> entry = (Entry<?,?>)object;
|
|
|
+ return ValueUtils.equals(entry.getKey(), getKey()) && ValueUtils.equals(entry.getValue(), getValue());
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+}
|