/** * $Id: ArrayOrderedMap.java 128 2014-03-18 22:27:33Z azeckoski $ * $URL: http://reflectutils.googlecode.com/svn/trunk/src/main/java/org/azeckoski/reflectutils/map/ArrayOrderedMap.java $ * ArrayOrderedMap.java - genericdao - May 5, 2008 2:16:35 PM - azeckoski ************************************************************************** * Copyright (c) 2008 Aaron Zeckoski * Licensed under the Apache License, Version 2 * * A copy of the Apache License, Version 2 has been included in this * distribution and is available at: http://www.apache.org/licenses/LICENSE-2.0.txt * * Aaron Zeckoski (azeckoski@gmail.com) (aaronz@vt.edu) (aaron@caret.cam.ac.uk) */ package org.azeckoski.reflectutils.map; import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.NoSuchElementException; import java.util.Set; /** * A map which keeps track of the order the entries are added * and allows retrieval of entries in the order they were entered as well, * this is NOT safe for multi-threaded access, * this is backed by a {@link HashMap} and {@link ArrayList} * * @author Aaron Zeckoski (azeckoski @ gmail.com) */ public class ArrayOrderedMap<K, V> extends HashMap<K, V> implements OrderedMap<K, V> { public static final long serialVersionUID = 1l; private ArrayList<K> list; public ArrayOrderedMap() { this(10); } public ArrayOrderedMap(int initialCapacity) { super(initialCapacity); list = new ArrayList<K>(initialCapacity); } public ArrayOrderedMap(Map<K, V> map) { this(map.size()); for (Entry<K, V> entry : map.entrySet()) { this.put(entry.getKey(), entry.getValue()); } } private String name = "entity"; public String getName() { return name; } /** * @param name the name to use when encoding this map of entities */ public void setName(String name) { this.name = name; } /** * @return a list of all the keys in this map in the order they were entered */ public List<K> getKeys() { return new ArrayList<K>(list); } /* (non-Javadoc) * @see org.azeckoski.reflectutils.map.OrderedMap#getValues() */ public List<V> getValues() { return new ArrayList<V>( values() ); } /** * @return a list of all the entries in this map in the order they were created */ public List<Entry<K, V>> getEntries() { ArrayList<Entry<K, V>> entries = new ArrayList<Entry<K,V>>(); for (K key : list) { Entry<K, V> entry = new SimpleEntry<K,V>(key, this.get(key)); entries.add(entry); } return entries; } /** * Get an entry based on the position it is in the map (based on the order entries were created) * @param position the position in the map (must be less that the size) * @return the entry at that position * @throws IllegalArgumentException if the position is greater than the map size */ public Entry<K, V> getEntry(int position) { if (position >= list.size()) { throw new IllegalArgumentException("Value is too large for the map size: " + list.size()); } K key = list.get(position); Entry<K, V> entry = new SimpleEntry<K,V>(key, this.get(key)); return entry; } @Override public V put(K key, V value) { V v = super.put(key, value); if (v != null) { // displaced list.remove(key); } list.add(key); return v; } @Override public void putAll(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) { put(entry.getKey(), entry.getValue()); } } @Override public V remove(Object key) { V v = super.remove(key); if (v != null) { list.remove(key); } return v; } @Override public void clear() { super.clear(); list.clear(); } public Enumeration<K> keys() { return new KeyIterator(); } public Enumeration<V> elements() { return new ValueIterator(); } transient Set<K> keySet; transient Set<Map.Entry<K,V>> entrySet; transient Collection<V> values; @Override public Set<K> keySet() { Set<K> ks = keySet; return (ks != null) ? ks : (keySet = new KeySet()); } @Override public Collection<V> values() { Collection<V> vs = values; return (vs != null) ? vs : (values = new Values()); } @Override @SuppressWarnings("unchecked") public Set<Map.Entry<K,V>> entrySet() { Set<Map.Entry<K,V>> es = entrySet; return (es != null) ? es : (entrySet = (Set<Map.Entry<K,V>>) (Set) new EntrySet()); } // Iterator support abstract class CoreIterator { int currentPos = -1; Entry<K, V> lastReturned = null; private List<Entry<K, V>> entries = ArrayOrderedMap.this.getEntries(); public boolean hasMore() { if ((currentPos + 1) < entries.size()) { return true; } return false; } public Entry<K, V> getNext() { currentPos++; try { lastReturned = entries.get(currentPos); } catch (RuntimeException e) { throw new NoSuchElementException("There are no more items available to get, the last one was reached"); } return lastReturned; } public void removeCurrent() { if (currentPos < 0) { throw new IllegalArgumentException("Have not called next yet, cannot remove from this iterator"); } entries.remove(currentPos); ArrayOrderedMap.this.remove(lastReturned.getKey()); } // shared methods public boolean hasNext() { return hasMore(); } public boolean hasMoreElements() { return hasMore(); } public void remove() { removeCurrent(); } } final class KeyIterator extends CoreIterator implements Iterator<K>, Enumeration<K> { public K next() { return super.getNext().getKey(); } public K nextElement() { return next(); } } final class ValueIterator extends CoreIterator implements Iterator<V>, Enumeration<V> { public V next() { return super.getNext().getValue(); } public V nextElement() { return next(); } } final class EntryIterator extends CoreIterator implements Iterator<Entry<K,V>>, Enumeration<Entry<K,V>> { public Entry<K,V> next() { return super.getNext(); } public Entry<K,V> nextElement() { return next(); } } // All below copied from CHM final class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return new KeyIterator(); } public int size() { return ArrayOrderedMap.this.size(); } public boolean contains(Object o) { return ArrayOrderedMap.this.containsKey(o); } public boolean remove(Object o) { return ArrayOrderedMap.this.remove(o) != null; } public void clear() { ArrayOrderedMap.this.clear(); } public Object[] toArray() { Collection<K> c = new ArrayList<K>(); for (Iterator<K> i = iterator(); i.hasNext(); ) c.add(i.next()); return c.toArray(); } public <T> T[] toArray(T[] a) { Collection<K> c = new ArrayList<K>(); for (Iterator<K> i = iterator(); i.hasNext(); ) c.add(i.next()); return c.toArray(a); } } final class Values extends AbstractCollection<V> { public Iterator<V> iterator() { return new ValueIterator(); } public int size() { return ArrayOrderedMap.this.size(); } public boolean contains(Object o) { return ArrayOrderedMap.this.containsValue(o); } public void clear() { ArrayOrderedMap.this.clear(); } public Object[] toArray() { Collection<V> c = new ArrayList<V>(); for (Iterator<V> i = iterator(); i.hasNext(); ) c.add(i.next()); return c.toArray(); } public <T> T[] toArray(T[] a) { Collection<V> c = new ArrayList<V>(); for (Iterator<V> i = iterator(); i.hasNext(); ) c.add(i.next()); return c.toArray(a); } } @SuppressWarnings("unchecked") final class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return new EntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> e = (Map.Entry<K,V>)o; V v = ArrayOrderedMap.this.get(e.getKey()); return v != null && v.equals(e.getValue()); } public boolean remove(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> e = (Map.Entry<K,V>)o; return ArrayOrderedMap.this.remove(e.getKey()) != null; } public int size() { return ArrayOrderedMap.this.size(); } public void clear() { ArrayOrderedMap.this.clear(); } public Object[] toArray() { // Since we don't ordinarily have distinct Entry objects, we // must pack elements using exportable SimpleEntry Collection<Map.Entry<K,V>> c = new ArrayList<Map.Entry<K,V>>(size()); for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) c.add(new SimpleEntry<K,V>(i.next())); return c.toArray(); } public <T> T[] toArray(T[] a) { Collection<Map.Entry<K,V>> c = new ArrayList<Map.Entry<K,V>>(size()); for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) c.add(new SimpleEntry<K,V>(i.next())); return c.toArray(a); } } /** * This duplicates java.util.AbstractMap.SimpleEntry until this class * is made accessible. */ static final class SimpleEntry<K,V> implements Entry<K,V> { K key; V value; public SimpleEntry(K key, V value) { this.key = key; this.value = value; } public SimpleEntry(Entry<K,V> e) { this.key = e.getKey(); this.value = e.getValue(); } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } @SuppressWarnings("unchecked") public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; return eq(key, e.getKey()) && eq(value, e.getValue()); } public int hashCode() { return ((key == null) ? 0 : key.hashCode()) ^ ((value == null) ? 0 : value.hashCode()); } public String toString() { return key + "=" + value; } static boolean eq(Object o1, Object o2) { return (o1 == null ? o2 == null : o1.equals(o2)); } } }