/* * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.util; import java.io.Serializable; import java.util.*; import java.util.Map.Entry; /** * Implementation of a Map that favors iteration speed rather than * get/put speed. * * @author Kirill Vainer */ public final class ListMap<K, V> extends AbstractMap<K, V> implements Cloneable, Serializable { private final static class ListMapEntry<K, V> implements Map.Entry<K, V>, Cloneable { private final K key; private V value; public ListMapEntry(K key, V value){ this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V v) { throw new UnsupportedOperationException(); } @Override public ListMapEntry<K, V> clone(){ return new ListMapEntry<K, V>(key, value); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final ListMapEntry<K, V> other = (ListMapEntry<K, V>) obj; if (this.key != other.key && (this.key == null || !this.key.equals(other.key))) { return false; } if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) { return false; } return true; } @Override public int hashCode() { return (this.key != null ? this.key.hashCode() : 0) ^ (this.value != null ? this.value.hashCode() : 0); } } private final HashMap<K, V> backingMap; private ListMapEntry<K, V>[] entries; // private final ArrayList<ListMapEntry<K,V>> entries; public ListMap(){ entries = new ListMapEntry[4]; backingMap = new HashMap<K, V>(4); // entries = new ArrayList<ListMapEntry<K,V>>(); } public ListMap(int initialCapacity){ entries = new ListMapEntry[initialCapacity]; backingMap = new HashMap<K, V>(initialCapacity); // entries = new ArrayList<ListMapEntry<K, V>>(initialCapacity); } public ListMap(Map<? extends K, ? extends V> map){ entries = new ListMapEntry[map.size()]; backingMap = new HashMap<K, V>(map.size()); // entries = new ArrayList<ListMapEntry<K, V>>(map.size()); putAll(map); } @Override public int size() { // return entries.size(); return backingMap.size(); } public Entry<K, V> getEntry(int index){ // return entries.get(index); return entries[index]; } public V getValue(int index){ // return entries.get(index).value; return entries[index].value; } public K getKey(int index){ // return entries.get(index).key; return entries[index].key; } @Override public boolean isEmpty() { return size() == 0; } private static boolean keyEq(Object keyA, Object keyB){ return keyA.hashCode() == keyB.hashCode() ? (keyA == keyB) || keyA.equals(keyB) : false; } // // private static boolean valEq(Object a, Object b){ // return a == null ? (b == null) : a.equals(b); // } @Override public boolean containsKey(Object key) { return backingMap.containsKey( (K) key); // if (key == null) // throw new IllegalArgumentException(); // // for (int i = 0; i < entries.size(); i++){ // ListMapEntry<K,V> entry = entries.get(i); // if (keyEq(entry.key, key)) // return true; // } // return false; } @Override public boolean containsValue(Object value) { return backingMap.containsValue( (V) value); // for (int i = 0; i < entries.size(); i++){ // if (valEq(entries.get(i).value, value)) // return true; // } // return false; } @Override public V get(Object key) { return backingMap.get( (K) key); // if (key == null) // throw new IllegalArgumentException(); // // for (int i = 0; i < entries.size(); i++){ // ListMapEntry<K,V> entry = entries.get(i); // if (keyEq(entry.key, key)) // return entry.value; // } // return null; } @Override public V put(K key, V value) { if (backingMap.containsKey(key)){ // set the value on the entry int size = size(); for (int i = 0; i < size; i++){ ListMapEntry<K, V> entry = entries[i]; if (keyEq(entry.key, key)){ entry.value = value; break; } } }else{ int size = size(); // expand list as necessary if (size == entries.length){ ListMapEntry<K, V>[] tmpEntries = entries; entries = new ListMapEntry[size * 2]; System.arraycopy(tmpEntries, 0, entries, 0, size); } entries[size] = new ListMapEntry<K, V>(key, value); } return backingMap.put(key, value); // if (key == null) // throw new IllegalArgumentException(); // // // check if entry exists, if yes, overwrite it with new value // for (int i = 0; i < entries.size(); i++){ // ListMapEntry<K,V> entry = entries.get(i); // if (keyEq(entry.key, key)){ // V prevValue = entry.value; // entry.value = value; // return prevValue; // } // } // // // add a new entry // entries.add(new ListMapEntry<K, V>(key, value)); // return null; } @Override public V remove(Object key) { V element = backingMap.remove( (K) key); if (element != null){ // find removed element int size = size() + 1; // includes removed element int removedIndex = -1; for (int i = 0; i < size; i++){ ListMapEntry<K, V> entry = entries[i]; if (keyEq(entry.key, key)){ removedIndex = i; break; } } assert removedIndex >= 0; size --; for (int i = removedIndex; i < size; i++){ entries[i] = entries[i+1]; } } return element; // if (key == null) // throw new IllegalArgumentException(); // // for (int i = 0; i < entries.size(); i++){ // ListMapEntry<K,V> entry = entries.get(i); // if (keyEq(entry.key, key)){ // return entries.remove(i).value; // } // } // return null; } @Override public void putAll(Map<? extends K, ? extends V> map) { for (Entry<? extends K, ? extends V> entry : map.entrySet()){ put(entry.getKey(), entry.getValue()); } // if (map instanceof ListMap){ // ListMap<K, V> listMap = (ListMap<K, V>) map; // ArrayList<ListMapEntry<K, V>> otherEntries = listMap.entries; // for (int i = 0; i < otherEntries.size(); i++){ // ListMapEntry<K, V> entry = otherEntries.get(i); // put(entry.key, entry.value); // } // }else{ // for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()){ // put(entry.getKey(), entry.getValue()); // } // } } @Override public void clear() { backingMap.clear(); // entries.clear(); } @Override public ListMap<K, V> clone(){ ListMap<K, V> clone = new ListMap<K, V>(size()); clone.putAll(this); return clone; } @Override public Set<K> keySet() { return backingMap.keySet(); // HashSet<K> keys = new HashSet<K>(); // for (int i = 0; i < entries.size(); i++){ // ListMapEntry<K,V> entry = entries.get(i); // keys.add(entry.key); // } // return keys; } @Override public Collection<V> values() { return backingMap.values(); // ArrayList<V> values = new ArrayList<V>(); // for (int i = 0; i < entries.size(); i++){ // ListMapEntry<K,V> entry = entries.get(i); // values.add(entry.value); // } // return values; } public Set<Entry<K, V>> entrySet() { return backingMap.entrySet(); // HashSet<Entry<K, V>> entryset = new HashSet<Entry<K, V>>(); // entryset.addAll(entries); // return entryset; } }