• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.math.MathUtils;
23 
24 /** An unordered map. This implementation is a cuckoo hash map using 3 hashes, random walking, and a small stash for problematic
25  * keys. Null keys are not allowed. Null values are allowed. No allocation is done except when growing the table size. <br>
26  * <br>
27  * This map performs very fast get, containsKey, and remove (typically O(1), worst case O(log(n))). Put may be a bit slower,
28  * depending on hash collisions. Load factors greater than 0.91 greatly increase the chances the map will have to rehash to the
29  * next higher POT size.
30  * @author Nathan Sweet */
31 public class ObjectMap<K, V> implements Iterable<ObjectMap.Entry<K, V>> {
32 	private static final int PRIME1 = 0xbe1f14b1;
33 	private static final int PRIME2 = 0xb4b82e39;
34 	private static final int PRIME3 = 0xced1c241;
35 
36 	public int size;
37 
38 	K[] keyTable;
39 	V[] valueTable;
40 	int capacity, stashSize;
41 
42 	private float loadFactor;
43 	private int hashShift, mask, threshold;
44 	private int stashCapacity;
45 	private int pushIterations;
46 
47 	private Entries entries1, entries2;
48 	private Values values1, values2;
49 	private Keys keys1, keys2;
50 
51 	/** Creates a new map with an initial capacity of 51 and a load factor of 0.8. */
ObjectMap()52 	public ObjectMap () {
53 		this(51, 0.8f);
54 	}
55 
56 	/** Creates a new map with a load factor of 0.8.
57 	 * @param initialCapacity If not a power of two, it is increased to the next nearest power of two. */
ObjectMap(int initialCapacity)58 	public ObjectMap (int initialCapacity) {
59 		this(initialCapacity, 0.8f);
60 	}
61 
62 	/** Creates a new map with the specified initial capacity and load factor. This map will hold initialCapacity items before
63 	 * growing the backing table.
64 	 * @param initialCapacity If not a power of two, it is increased to the next nearest power of two. */
ObjectMap(int initialCapacity, float loadFactor)65 	public ObjectMap (int initialCapacity, float loadFactor) {
66 		if (initialCapacity < 0) throw new IllegalArgumentException("initialCapacity must be >= 0: " + initialCapacity);
67 		initialCapacity = MathUtils.nextPowerOfTwo((int)Math.ceil(initialCapacity / loadFactor));
68 		if (initialCapacity > 1 << 30) throw new IllegalArgumentException("initialCapacity is too large: " + initialCapacity);
69 		capacity = initialCapacity;
70 
71 		if (loadFactor <= 0) throw new IllegalArgumentException("loadFactor must be > 0: " + loadFactor);
72 		this.loadFactor = loadFactor;
73 
74 		threshold = (int)(capacity * loadFactor);
75 		mask = capacity - 1;
76 		hashShift = 31 - Integer.numberOfTrailingZeros(capacity);
77 		stashCapacity = Math.max(3, (int)Math.ceil(Math.log(capacity)) * 2);
78 		pushIterations = Math.max(Math.min(capacity, 8), (int)Math.sqrt(capacity) / 8);
79 
80 		keyTable = (K[])new Object[capacity + stashCapacity];
81 		valueTable = (V[])new Object[keyTable.length];
82 	}
83 
84 	/** Creates a new map identical to the specified map. */
ObjectMap(ObjectMap<? extends K, ? extends V> map)85 	public ObjectMap (ObjectMap<? extends K, ? extends V> map) {
86 		this((int)Math.floor(map.capacity * map.loadFactor), map.loadFactor);
87 		stashSize = map.stashSize;
88 		System.arraycopy(map.keyTable, 0, keyTable, 0, map.keyTable.length);
89 		System.arraycopy(map.valueTable, 0, valueTable, 0, map.valueTable.length);
90 		size = map.size;
91 	}
92 
93 	/** Returns the old value associated with the specified key, or null. */
put(K key, V value)94 	public V put (K key, V value) {
95 		if (key == null) throw new IllegalArgumentException("key cannot be null.");
96 		return put_internal(key, value);
97 	}
98 
put_internal(K key, V value)99 	private V put_internal (K key, V value) {
100 		K[] keyTable = this.keyTable;
101 
102 		// Check for existing keys.
103 		int hashCode = key.hashCode();
104 		int index1 = hashCode & mask;
105 		K key1 = keyTable[index1];
106 		if (key.equals(key1)) {
107 			V oldValue = valueTable[index1];
108 			valueTable[index1] = value;
109 			return oldValue;
110 		}
111 
112 		int index2 = hash2(hashCode);
113 		K key2 = keyTable[index2];
114 		if (key.equals(key2)) {
115 			V oldValue = valueTable[index2];
116 			valueTable[index2] = value;
117 			return oldValue;
118 		}
119 
120 		int index3 = hash3(hashCode);
121 		K key3 = keyTable[index3];
122 		if (key.equals(key3)) {
123 			V oldValue = valueTable[index3];
124 			valueTable[index3] = value;
125 			return oldValue;
126 		}
127 
128 		// Update key in the stash.
129 		for (int i = capacity, n = i + stashSize; i < n; i++) {
130 			if (key.equals(keyTable[i])) {
131 				V oldValue = valueTable[i];
132 				valueTable[i] = value;
133 				return oldValue;
134 			}
135 		}
136 
137 		// Check for empty buckets.
138 		if (key1 == null) {
139 			keyTable[index1] = key;
140 			valueTable[index1] = value;
141 			if (size++ >= threshold) resize(capacity << 1);
142 			return null;
143 		}
144 
145 		if (key2 == null) {
146 			keyTable[index2] = key;
147 			valueTable[index2] = value;
148 			if (size++ >= threshold) resize(capacity << 1);
149 			return null;
150 		}
151 
152 		if (key3 == null) {
153 			keyTable[index3] = key;
154 			valueTable[index3] = value;
155 			if (size++ >= threshold) resize(capacity << 1);
156 			return null;
157 		}
158 
159 		push(key, value, index1, key1, index2, key2, index3, key3);
160 		return null;
161 	}
162 
putAll(ObjectMap<K, V> map)163 	public void putAll (ObjectMap<K, V> map) {
164 		ensureCapacity(map.size);
165 		for (Entry<K, V> entry : map)
166 			put(entry.key, entry.value);
167 	}
168 
169 	/** Skips checks for existing keys. */
putResize(K key, V value)170 	private void putResize (K key, V value) {
171 		// Check for empty buckets.
172 		int hashCode = key.hashCode();
173 		int index1 = hashCode & mask;
174 		K key1 = keyTable[index1];
175 		if (key1 == null) {
176 			keyTable[index1] = key;
177 			valueTable[index1] = value;
178 			if (size++ >= threshold) resize(capacity << 1);
179 			return;
180 		}
181 
182 		int index2 = hash2(hashCode);
183 		K key2 = keyTable[index2];
184 		if (key2 == null) {
185 			keyTable[index2] = key;
186 			valueTable[index2] = value;
187 			if (size++ >= threshold) resize(capacity << 1);
188 			return;
189 		}
190 
191 		int index3 = hash3(hashCode);
192 		K key3 = keyTable[index3];
193 		if (key3 == null) {
194 			keyTable[index3] = key;
195 			valueTable[index3] = value;
196 			if (size++ >= threshold) resize(capacity << 1);
197 			return;
198 		}
199 
200 		push(key, value, index1, key1, index2, key2, index3, key3);
201 	}
202 
push(K insertKey, V insertValue, int index1, K key1, int index2, K key2, int index3, K key3)203 	private void push (K insertKey, V insertValue, int index1, K key1, int index2, K key2, int index3, K key3) {
204 		K[] keyTable = this.keyTable;
205 		V[] valueTable = this.valueTable;
206 		int mask = this.mask;
207 
208 		// Push keys until an empty bucket is found.
209 		K evictedKey;
210 		V evictedValue;
211 		int i = 0, pushIterations = this.pushIterations;
212 		do {
213 			// Replace the key and value for one of the hashes.
214 			switch (MathUtils.random(2)) {
215 			case 0:
216 				evictedKey = key1;
217 				evictedValue = valueTable[index1];
218 				keyTable[index1] = insertKey;
219 				valueTable[index1] = insertValue;
220 				break;
221 			case 1:
222 				evictedKey = key2;
223 				evictedValue = valueTable[index2];
224 				keyTable[index2] = insertKey;
225 				valueTable[index2] = insertValue;
226 				break;
227 			default:
228 				evictedKey = key3;
229 				evictedValue = valueTable[index3];
230 				keyTable[index3] = insertKey;
231 				valueTable[index3] = insertValue;
232 				break;
233 			}
234 
235 			// If the evicted key hashes to an empty bucket, put it there and stop.
236 			int hashCode = evictedKey.hashCode();
237 			index1 = hashCode & mask;
238 			key1 = keyTable[index1];
239 			if (key1 == null) {
240 				keyTable[index1] = evictedKey;
241 				valueTable[index1] = evictedValue;
242 				if (size++ >= threshold) resize(capacity << 1);
243 				return;
244 			}
245 
246 			index2 = hash2(hashCode);
247 			key2 = keyTable[index2];
248 			if (key2 == null) {
249 				keyTable[index2] = evictedKey;
250 				valueTable[index2] = evictedValue;
251 				if (size++ >= threshold) resize(capacity << 1);
252 				return;
253 			}
254 
255 			index3 = hash3(hashCode);
256 			key3 = keyTable[index3];
257 			if (key3 == null) {
258 				keyTable[index3] = evictedKey;
259 				valueTable[index3] = evictedValue;
260 				if (size++ >= threshold) resize(capacity << 1);
261 				return;
262 			}
263 
264 			if (++i == pushIterations) break;
265 
266 			insertKey = evictedKey;
267 			insertValue = evictedValue;
268 		} while (true);
269 
270 		putStash(evictedKey, evictedValue);
271 	}
272 
putStash(K key, V value)273 	private void putStash (K key, V value) {
274 		if (stashSize == stashCapacity) {
275 			// Too many pushes occurred and the stash is full, increase the table size.
276 			resize(capacity << 1);
277 			put_internal(key, value);
278 			return;
279 		}
280 		// Store key in the stash.
281 		int index = capacity + stashSize;
282 		keyTable[index] = key;
283 		valueTable[index] = value;
284 		stashSize++;
285 		size++;
286 	}
287 
get(K key)288 	public V get (K key) {
289 		int hashCode = key.hashCode();
290 		int index = hashCode & mask;
291 		if (!key.equals(keyTable[index])) {
292 			index = hash2(hashCode);
293 			if (!key.equals(keyTable[index])) {
294 				index = hash3(hashCode);
295 				if (!key.equals(keyTable[index])) return getStash(key);
296 			}
297 		}
298 		return valueTable[index];
299 	}
300 
getStash(K key)301 	private V getStash (K key) {
302 		K[] keyTable = this.keyTable;
303 		for (int i = capacity, n = i + stashSize; i < n; i++)
304 			if (key.equals(keyTable[i])) return valueTable[i];
305 		return null;
306 	}
307 
308 	/** Returns the value for the specified key, or the default value if the key is not in the map. */
get(K key, V defaultValue)309 	public V get (K key, V defaultValue) {
310 		int hashCode = key.hashCode();
311 		int index = hashCode & mask;
312 		if (!key.equals(keyTable[index])) {
313 			index = hash2(hashCode);
314 			if (!key.equals(keyTable[index])) {
315 				index = hash3(hashCode);
316 				if (!key.equals(keyTable[index])) return getStash(key, defaultValue);
317 			}
318 		}
319 		return valueTable[index];
320 	}
321 
getStash(K key, V defaultValue)322 	private V getStash (K key, V defaultValue) {
323 		K[] keyTable = this.keyTable;
324 		for (int i = capacity, n = i + stashSize; i < n; i++)
325 			if (key.equals(keyTable[i])) return valueTable[i];
326 		return defaultValue;
327 	}
328 
remove(K key)329 	public V remove (K key) {
330 		int hashCode = key.hashCode();
331 		int index = hashCode & mask;
332 		if (key.equals(keyTable[index])) {
333 			keyTable[index] = null;
334 			V oldValue = valueTable[index];
335 			valueTable[index] = null;
336 			size--;
337 			return oldValue;
338 		}
339 
340 		index = hash2(hashCode);
341 		if (key.equals(keyTable[index])) {
342 			keyTable[index] = null;
343 			V oldValue = valueTable[index];
344 			valueTable[index] = null;
345 			size--;
346 			return oldValue;
347 		}
348 
349 		index = hash3(hashCode);
350 		if (key.equals(keyTable[index])) {
351 			keyTable[index] = null;
352 			V oldValue = valueTable[index];
353 			valueTable[index] = null;
354 			size--;
355 			return oldValue;
356 		}
357 
358 		return removeStash(key);
359 	}
360 
removeStash(K key)361 	V removeStash (K key) {
362 		K[] keyTable = this.keyTable;
363 		for (int i = capacity, n = i + stashSize; i < n; i++) {
364 			if (key.equals(keyTable[i])) {
365 				V oldValue = valueTable[i];
366 				removeStashIndex(i);
367 				size--;
368 				return oldValue;
369 			}
370 		}
371 		return null;
372 	}
373 
removeStashIndex(int index)374 	void removeStashIndex (int index) {
375 		// If the removed location was not last, move the last tuple to the removed location.
376 		stashSize--;
377 		int lastIndex = capacity + stashSize;
378 		if (index < lastIndex) {
379 			keyTable[index] = keyTable[lastIndex];
380 			valueTable[index] = valueTable[lastIndex];
381 			valueTable[lastIndex] = null;
382 		} else
383 			valueTable[index] = null;
384 	}
385 
386 	/** Reduces the size of the backing arrays to be the specified capacity or less. If the capacity is already less, nothing is
387 	 * done. If the map contains more items than the specified capacity, the next highest power of two capacity is used instead. */
shrink(int maximumCapacity)388 	public void shrink (int maximumCapacity) {
389 		if (maximumCapacity < 0) throw new IllegalArgumentException("maximumCapacity must be >= 0: " + maximumCapacity);
390 		if (size > maximumCapacity) maximumCapacity = size;
391 		if (capacity <= maximumCapacity) return;
392 		maximumCapacity = MathUtils.nextPowerOfTwo(maximumCapacity);
393 		resize(maximumCapacity);
394 	}
395 
396 	/** Clears the map and reduces the size of the backing arrays to be the specified capacity if they are larger. */
clear(int maximumCapacity)397 	public void clear (int maximumCapacity) {
398 		if (capacity <= maximumCapacity) {
399 			clear();
400 			return;
401 		}
402 		size = 0;
403 		resize(maximumCapacity);
404 	}
405 
clear()406 	public void clear () {
407 		if (size == 0) return;
408 		K[] keyTable = this.keyTable;
409 		V[] valueTable = this.valueTable;
410 		for (int i = capacity + stashSize; i-- > 0;) {
411 			keyTable[i] = null;
412 			valueTable[i] = null;
413 		}
414 		size = 0;
415 		stashSize = 0;
416 	}
417 
418 	/** Returns true if the specified value is in the map. Note this traverses the entire map and compares every value, which may
419 	 * be an expensive operation.
420 	 * @param identity If true, uses == to compare the specified value with values in the map. If false, uses
421 	 *           {@link #equals(Object)}. */
containsValue(Object value, boolean identity)422 	public boolean containsValue (Object value, boolean identity) {
423 		V[] valueTable = this.valueTable;
424 		if (value == null) {
425 			K[] keyTable = this.keyTable;
426 			for (int i = capacity + stashSize; i-- > 0;)
427 				if (keyTable[i] != null && valueTable[i] == null) return true;
428 		} else if (identity) {
429 			for (int i = capacity + stashSize; i-- > 0;)
430 				if (valueTable[i] == value) return true;
431 		} else {
432 			for (int i = capacity + stashSize; i-- > 0;)
433 				if (value.equals(valueTable[i])) return true;
434 		}
435 		return false;
436 	}
437 
containsKey(K key)438 	public boolean containsKey (K key) {
439 		int hashCode = key.hashCode();
440 		int index = hashCode & mask;
441 		if (!key.equals(keyTable[index])) {
442 			index = hash2(hashCode);
443 			if (!key.equals(keyTable[index])) {
444 				index = hash3(hashCode);
445 				if (!key.equals(keyTable[index])) return containsKeyStash(key);
446 			}
447 		}
448 		return true;
449 	}
450 
containsKeyStash(K key)451 	private boolean containsKeyStash (K key) {
452 		K[] keyTable = this.keyTable;
453 		for (int i = capacity, n = i + stashSize; i < n; i++)
454 			if (key.equals(keyTable[i])) return true;
455 		return false;
456 	}
457 
458 	/** Returns the key for the specified value, or null if it is not in the map. Note this traverses the entire map and compares
459 	 * every value, which may be an expensive operation.
460 	 * @param identity If true, uses == to compare the specified value with values in the map. If false, uses
461 	 *           {@link #equals(Object)}. */
findKey(Object value, boolean identity)462 	public K findKey (Object value, boolean identity) {
463 		V[] valueTable = this.valueTable;
464 		if (value == null) {
465 			K[] keyTable = this.keyTable;
466 			for (int i = capacity + stashSize; i-- > 0;)
467 				if (keyTable[i] != null && valueTable[i] == null) return keyTable[i];
468 		} else if (identity) {
469 			for (int i = capacity + stashSize; i-- > 0;)
470 				if (valueTable[i] == value) return keyTable[i];
471 		} else {
472 			for (int i = capacity + stashSize; i-- > 0;)
473 				if (value.equals(valueTable[i])) return keyTable[i];
474 		}
475 		return null;
476 	}
477 
478 	/** Increases the size of the backing array to accommodate the specified number of additional items. Useful before adding many
479 	 * items to avoid multiple backing array resizes. */
ensureCapacity(int additionalCapacity)480 	public void ensureCapacity (int additionalCapacity) {
481 		int sizeNeeded = size + additionalCapacity;
482 		if (sizeNeeded >= threshold) resize(MathUtils.nextPowerOfTwo((int)Math.ceil(sizeNeeded / loadFactor)));
483 	}
484 
resize(int newSize)485 	private void resize (int newSize) {
486 		int oldEndIndex = capacity + stashSize;
487 
488 		capacity = newSize;
489 		threshold = (int)(newSize * loadFactor);
490 		mask = newSize - 1;
491 		hashShift = 31 - Integer.numberOfTrailingZeros(newSize);
492 		stashCapacity = Math.max(3, (int)Math.ceil(Math.log(newSize)) * 2);
493 		pushIterations = Math.max(Math.min(newSize, 8), (int)Math.sqrt(newSize) / 8);
494 
495 		K[] oldKeyTable = keyTable;
496 		V[] oldValueTable = valueTable;
497 
498 		keyTable = (K[])new Object[newSize + stashCapacity];
499 		valueTable = (V[])new Object[newSize + stashCapacity];
500 
501 		int oldSize = size;
502 		size = 0;
503 		stashSize = 0;
504 		if (oldSize > 0) {
505 			for (int i = 0; i < oldEndIndex; i++) {
506 				K key = oldKeyTable[i];
507 				if (key != null) putResize(key, oldValueTable[i]);
508 			}
509 		}
510 	}
511 
hash2(int h)512 	private int hash2 (int h) {
513 		h *= PRIME2;
514 		return (h ^ h >>> hashShift) & mask;
515 	}
516 
hash3(int h)517 	private int hash3 (int h) {
518 		h *= PRIME3;
519 		return (h ^ h >>> hashShift) & mask;
520 	}
521 
hashCode()522 	public int hashCode () {
523 		int h = 0;
524 		K[] keyTable = this.keyTable;
525 		V[] valueTable = this.valueTable;
526 		for (int i = 0, n = capacity + stashSize; i < n; i++) {
527 			K key = keyTable[i];
528 			if (key != null) {
529 				h += key.hashCode() * 31;
530 
531 				V value = valueTable[i];
532 				if (value != null) {
533 					h += value.hashCode();
534 				}
535 			}
536 		}
537 		return h;
538 	}
539 
equals(Object obj)540 	public boolean equals (Object obj) {
541 		if (obj == this) return true;
542 		if (!(obj instanceof ObjectMap)) return false;
543 		ObjectMap<K, V> other = (ObjectMap)obj;
544 		if (other.size != size) return false;
545 		K[] keyTable = this.keyTable;
546 		V[] valueTable = this.valueTable;
547 		for (int i = 0, n = capacity + stashSize; i < n; i++) {
548 			K key = keyTable[i];
549 			if (key != null) {
550 				V value = valueTable[i];
551 				if (value == null) {
552 					if (!other.containsKey(key) || other.get(key) != null) {
553 						return false;
554 					}
555 				} else {
556 					if (!value.equals(other.get(key))) {
557 						return false;
558 					}
559 				}
560 			}
561 		}
562 		return true;
563 	}
564 
toString(String separator)565 	public String toString (String separator) {
566 		return toString(separator, false);
567 	}
568 
toString()569 	public String toString () {
570 		return toString(", ", true);
571 	}
572 
toString(String separator, boolean braces)573 	private String toString (String separator, boolean braces) {
574 		if (size == 0) return braces ? "{}" : "";
575 		StringBuilder buffer = new StringBuilder(32);
576 		if (braces) buffer.append('{');
577 		K[] keyTable = this.keyTable;
578 		V[] valueTable = this.valueTable;
579 		int i = keyTable.length;
580 		while (i-- > 0) {
581 			K key = keyTable[i];
582 			if (key == null) continue;
583 			buffer.append(key);
584 			buffer.append('=');
585 			buffer.append(valueTable[i]);
586 			break;
587 		}
588 		while (i-- > 0) {
589 			K key = keyTable[i];
590 			if (key == null) continue;
591 			buffer.append(separator);
592 			buffer.append(key);
593 			buffer.append('=');
594 			buffer.append(valueTable[i]);
595 		}
596 		if (braces) buffer.append('}');
597 		return buffer.toString();
598 	}
599 
iterator()600 	public Entries<K, V> iterator () {
601 		return entries();
602 	}
603 
604 	/** Returns an iterator for the entries in the map. Remove is supported. Note that the same iterator instance is returned each
605 	 * time this method is called. Use the {@link Entries} constructor for nested or multithreaded iteration. */
entries()606 	public Entries<K, V> entries () {
607 		if (entries1 == null) {
608 			entries1 = new Entries(this);
609 			entries2 = new Entries(this);
610 		}
611 		if (!entries1.valid) {
612 			entries1.reset();
613 			entries1.valid = true;
614 			entries2.valid = false;
615 			return entries1;
616 		}
617 		entries2.reset();
618 		entries2.valid = true;
619 		entries1.valid = false;
620 		return entries2;
621 	}
622 
623 	/** Returns an iterator for the values in the map. Remove is supported. Note that the same iterator instance is returned each
624 	 * time this method is called. Use the {@link Values} constructor for nested or multithreaded iteration. */
values()625 	public Values<V> values () {
626 		if (values1 == null) {
627 			values1 = new Values(this);
628 			values2 = new Values(this);
629 		}
630 		if (!values1.valid) {
631 			values1.reset();
632 			values1.valid = true;
633 			values2.valid = false;
634 			return values1;
635 		}
636 		values2.reset();
637 		values2.valid = true;
638 		values1.valid = false;
639 		return values2;
640 	}
641 
642 	/** Returns an iterator for the keys in the map. Remove is supported. Note that the same iterator instance is returned each
643 	 * time this method is called. Use the {@link Keys} constructor for nested or multithreaded iteration. */
keys()644 	public Keys<K> keys () {
645 		if (keys1 == null) {
646 			keys1 = new Keys(this);
647 			keys2 = new Keys(this);
648 		}
649 		if (!keys1.valid) {
650 			keys1.reset();
651 			keys1.valid = true;
652 			keys2.valid = false;
653 			return keys1;
654 		}
655 		keys2.reset();
656 		keys2.valid = true;
657 		keys1.valid = false;
658 		return keys2;
659 	}
660 
661 	static public class Entry<K, V> {
662 		public K key;
663 		public V value;
664 
toString()665 		public String toString () {
666 			return key + "=" + value;
667 		}
668 	}
669 
670 	static private abstract class MapIterator<K, V, I> implements Iterable<I>, Iterator<I> {
671 		public boolean hasNext;
672 
673 		final ObjectMap<K, V> map;
674 		int nextIndex, currentIndex;
675 		boolean valid = true;
676 
MapIterator(ObjectMap<K, V> map)677 		public MapIterator (ObjectMap<K, V> map) {
678 			this.map = map;
679 			reset();
680 		}
681 
reset()682 		public void reset () {
683 			currentIndex = -1;
684 			nextIndex = -1;
685 			findNextIndex();
686 		}
687 
findNextIndex()688 		void findNextIndex () {
689 			hasNext = false;
690 			K[] keyTable = map.keyTable;
691 			for (int n = map.capacity + map.stashSize; ++nextIndex < n;) {
692 				if (keyTable[nextIndex] != null) {
693 					hasNext = true;
694 					break;
695 				}
696 			}
697 		}
698 
remove()699 		public void remove () {
700 			if (currentIndex < 0) throw new IllegalStateException("next must be called before remove.");
701 			if (currentIndex >= map.capacity) {
702 				map.removeStashIndex(currentIndex);
703 				nextIndex = currentIndex - 1;
704 				findNextIndex();
705 			} else {
706 				map.keyTable[currentIndex] = null;
707 				map.valueTable[currentIndex] = null;
708 			}
709 			currentIndex = -1;
710 			map.size--;
711 		}
712 	}
713 
714 	static public class Entries<K, V> extends MapIterator<K, V, Entry<K, V>> {
715 		Entry<K, V> entry = new Entry();
716 
Entries(ObjectMap<K, V> map)717 		public Entries (ObjectMap<K, V> map) {
718 			super(map);
719 		}
720 
721 		/** Note the same entry instance is returned each time this method is called. */
next()722 		public Entry<K, V> next () {
723 			if (!hasNext) throw new NoSuchElementException();
724 			if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested.");
725 			K[] keyTable = map.keyTable;
726 			entry.key = keyTable[nextIndex];
727 			entry.value = map.valueTable[nextIndex];
728 			currentIndex = nextIndex;
729 			findNextIndex();
730 			return entry;
731 		}
732 
hasNext()733 		public boolean hasNext () {
734 			if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested.");
735 			return hasNext;
736 		}
737 
iterator()738 		public Entries<K, V> iterator () {
739 			return this;
740 		}
741 	}
742 
743 	static public class Values<V> extends MapIterator<Object, V, V> {
Values(ObjectMap<?, V> map)744 		public Values (ObjectMap<?, V> map) {
745 			super((ObjectMap<Object, V>)map);
746 		}
747 
hasNext()748 		public boolean hasNext () {
749 			if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested.");
750 			return hasNext;
751 		}
752 
next()753 		public V next () {
754 			if (!hasNext) throw new NoSuchElementException();
755 			if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested.");
756 			V value = map.valueTable[nextIndex];
757 			currentIndex = nextIndex;
758 			findNextIndex();
759 			return value;
760 		}
761 
iterator()762 		public Values<V> iterator () {
763 			return this;
764 		}
765 
766 		/** Returns a new array containing the remaining values. */
toArray()767 		public Array<V> toArray () {
768 			return toArray(new Array(true, map.size));
769 		}
770 
771 		/** Adds the remaining values to the specified array. */
toArray(Array<V> array)772 		public Array<V> toArray (Array<V> array) {
773 			while (hasNext)
774 				array.add(next());
775 			return array;
776 		}
777 	}
778 
779 	static public class Keys<K> extends MapIterator<K, Object, K> {
Keys(ObjectMap<K, ?> map)780 		public Keys (ObjectMap<K, ?> map) {
781 			super((ObjectMap<K, Object>)map);
782 		}
783 
hasNext()784 		public boolean hasNext () {
785 			if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested.");
786 			return hasNext;
787 		}
788 
next()789 		public K next () {
790 			if (!hasNext) throw new NoSuchElementException();
791 			if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested.");
792 			K key = map.keyTable[nextIndex];
793 			currentIndex = nextIndex;
794 			findNextIndex();
795 			return key;
796 		}
797 
iterator()798 		public Keys<K> iterator () {
799 			return this;
800 		}
801 
802 		/** Returns a new array containing the remaining keys. */
toArray()803 		public Array<K> toArray () {
804 			return toArray(new Array(true, map.size));
805 		}
806 
807 		/** Adds the remaining keys to the array. */
toArray(Array<K> array)808 		public Array<K> toArray (Array<K> array) {
809 			while (hasNext)
810 				array.add(next());
811 			return array;
812 		}
813 	}
814 }
815