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