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