1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 17 package com.badlogic.gdx.utils; 18 19 import java.util.Iterator; 20 import java.util.NoSuchElementException; 21 22 import com.badlogic.gdx.utils.ObjectMap.Entries; 23 24 /** An {@link ObjectMap} that also stores keys in an {@link Array} using the insertion order. There is some additional overhead for 25 * put and remove. Iteration over the {@link #entries()}, {@link #keys()}, and {@link #values()} is ordered and faster than an 26 * unordered map. Keys can also be accessed and the order changed using {@link #orderedKeys()}. 27 * @author Nathan Sweet */ 28 public class OrderedMap<K, V> extends ObjectMap<K, V> { 29 final Array<K> keys; 30 31 private Entries entries1, entries2; 32 private Values values1, values2; 33 private Keys keys1, keys2; 34 OrderedMap()35 public OrderedMap () { 36 keys = new Array(); 37 } 38 OrderedMap(int initialCapacity)39 public OrderedMap (int initialCapacity) { 40 super(initialCapacity); 41 keys = new Array(capacity); 42 } 43 OrderedMap(int initialCapacity, float loadFactor)44 public OrderedMap (int initialCapacity, float loadFactor) { 45 super(initialCapacity, loadFactor); 46 keys = new Array(capacity); 47 } 48 OrderedMap(ObjectMap<? extends K, ? extends V> map)49 public OrderedMap (ObjectMap<? extends K, ? extends V> map) { 50 super(map); 51 keys = new Array(capacity); 52 } 53 put(K key, V value)54 public V put (K key, V value) { 55 if (!containsKey(key)) keys.add(key); 56 return super.put(key, value); 57 } 58 remove(K key)59 public V remove (K key) { 60 keys.removeValue(key, false); 61 return super.remove(key); 62 } 63 clear(int maximumCapacity)64 public void clear (int maximumCapacity) { 65 keys.clear(); 66 super.clear(maximumCapacity); 67 } 68 clear()69 public void clear () { 70 keys.clear(); 71 super.clear(); 72 } 73 orderedKeys()74 public Array<K> orderedKeys () { 75 return keys; 76 } 77 iterator()78 public Entries<K, V> iterator () { 79 return entries(); 80 } 81 82 /** Returns an iterator for the entries in the map. Remove is supported. Note that the same iterator instance is returned each 83 * time this method is called. Use the {@link OrderedMapEntries} constructor for nested or multithreaded iteration. */ entries()84 public Entries<K, V> entries () { 85 if (entries1 == null) { 86 entries1 = new OrderedMapEntries(this); 87 entries2 = new OrderedMapEntries(this); 88 } 89 if (!entries1.valid) { 90 entries1.reset(); 91 entries1.valid = true; 92 entries2.valid = false; 93 return entries1; 94 } 95 entries2.reset(); 96 entries2.valid = true; 97 entries1.valid = false; 98 return entries2; 99 } 100 101 /** Returns an iterator for the values in the map. Remove is supported. Note that the same iterator instance is returned each 102 * time this method is called. Use the {@link OrderedMapValues} constructor for nested or multithreaded iteration. */ values()103 public Values<V> values () { 104 if (values1 == null) { 105 values1 = new OrderedMapValues(this); 106 values2 = new OrderedMapValues(this); 107 } 108 if (!values1.valid) { 109 values1.reset(); 110 values1.valid = true; 111 values2.valid = false; 112 return values1; 113 } 114 values2.reset(); 115 values2.valid = true; 116 values1.valid = false; 117 return values2; 118 } 119 120 /** Returns an iterator for the keys in the map. Remove is supported. Note that the same iterator instance is returned each time 121 * this method is called. Use the {@link OrderedMapKeys} constructor for nested or multithreaded iteration. */ keys()122 public Keys<K> keys () { 123 if (keys1 == null) { 124 keys1 = new OrderedMapKeys(this); 125 keys2 = new OrderedMapKeys(this); 126 } 127 if (!keys1.valid) { 128 keys1.reset(); 129 keys1.valid = true; 130 keys2.valid = false; 131 return keys1; 132 } 133 keys2.reset(); 134 keys2.valid = true; 135 keys1.valid = false; 136 return keys2; 137 } 138 toString()139 public String toString () { 140 if (size == 0) return "{}"; 141 StringBuilder buffer = new StringBuilder(32); 142 buffer.append('{'); 143 Array<K> keys = this.keys; 144 for (int i = 0, n = keys.size; i < n; i++) { 145 K key = keys.get(i); 146 if (i > 0) buffer.append(", "); 147 buffer.append(key); 148 buffer.append('='); 149 buffer.append(get(key)); 150 } 151 buffer.append('}'); 152 return buffer.toString(); 153 } 154 155 static public class OrderedMapEntries<K, V> extends Entries<K, V> { 156 private Array<K> keys; 157 OrderedMapEntries(OrderedMap<K, V> map)158 public OrderedMapEntries (OrderedMap<K, V> map) { 159 super(map); 160 keys = map.keys; 161 } 162 reset()163 public void reset () { 164 nextIndex = 0; 165 hasNext = map.size > 0; 166 } 167 next()168 public Entry next () { 169 if (!hasNext) throw new NoSuchElementException(); 170 if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested."); 171 entry.key = keys.get(nextIndex); 172 entry.value = map.get(entry.key); 173 nextIndex++; 174 hasNext = nextIndex < map.size; 175 return entry; 176 } 177 178 public void remove () { 179 if (currentIndex < 0) throw new IllegalStateException("next must be called before remove."); 180 map.remove(entry.key); 181 } 182 } 183 184 static public class OrderedMapKeys<K> extends Keys<K> { 185 private Array<K> keys; 186 187 public OrderedMapKeys (OrderedMap<K, ?> map) { 188 super(map); 189 keys = map.keys; 190 } 191 192 public void reset () { 193 nextIndex = 0; 194 hasNext = map.size > 0; 195 } 196 197 public K next () { 198 if (!hasNext) throw new NoSuchElementException(); 199 if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested."); 200 K key = keys.get(nextIndex); 201 nextIndex++; 202 hasNext = nextIndex < map.size; 203 return key; 204 } 205 206 public void remove () { 207 if (currentIndex < 0) throw new IllegalStateException("next must be called before remove."); 208 map.remove(keys.get(nextIndex - 1)); 209 } 210 } 211 212 static public class OrderedMapValues<V> extends Values<V> { 213 private Array keys; 214 215 public OrderedMapValues (OrderedMap<?, V> map) { 216 super(map); 217 keys = map.keys; 218 } 219 220 public void reset () { 221 nextIndex = 0; 222 hasNext = map.size > 0; 223 } 224 225 public V next () { 226 if (!hasNext) throw new NoSuchElementException(); 227 if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested."); 228 V value = (V)map.get(keys.get(nextIndex)); 229 nextIndex++; 230 hasNext = nextIndex < map.size; 231 return value; 232 } 233 234 public void remove () { 235 if (currentIndex < 0) throw new IllegalStateException("next must be called before remove."); 236 map.remove(keys.get(nextIndex - 1)); 237 } 238 } 239 } 240