001package org.maltparser.core.helper; 002 003/* 004 * Copyright 2009 Google Inc. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 007 * use this file except in compliance with the License. You may obtain a copy of 008 * the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 014 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 015 * License for the specific language governing permissions and limitations under 016 * the License. 017 */ 018 019import java.io.IOException; 020import java.io.ObjectInputStream; 021import java.io.ObjectOutputStream; 022import java.io.Serializable; 023import java.util.AbstractCollection; 024import java.util.AbstractSet; 025import java.util.Collection; 026import java.util.Iterator; 027import java.util.Map; 028import java.util.NoSuchElementException; 029import java.util.Set; 030 031/** 032 * A memory-efficient hash map. 033 * 034 * @param <K> the key type 035 * @param <V> the value type 036 */ 037public class HashMap<K, V> implements Map<K, V>, Serializable { 038 private static final long serialVersionUID = 7526471155622776147L; 039 /** 040 * In the interest of memory-savings, we start with the smallest feasible 041 * power-of-two table size that can hold three items without rehashing. If we 042 * started with a size of 2, we'd have to expand as soon as the second item 043 * was added. 044 */ 045 private static final int INITIAL_TABLE_SIZE = 4; 046 047 private class EntryIterator implements Iterator<Entry<K, V>> { 048 private int index = 0; 049 private int last = -1; 050 051 { 052 advanceToItem(); 053 } 054 055 public boolean hasNext() { 056 return index < keys.length; 057 } 058 059 public Entry<K, V> next() { 060 if (!hasNext()) { 061 throw new NoSuchElementException(); 062 } 063 last = index; 064 Entry<K, V> toReturn = new HashEntry(index++); 065 advanceToItem(); 066 return toReturn; 067 } 068 069 public void remove() { 070 if (last < 0) { 071 throw new IllegalStateException(); 072 } 073 internalRemove(last); 074 if (keys[last] != null) { 075 index = last; 076 } 077 last = -1; 078 } 079 080 private void advanceToItem() { 081 for (; index < keys.length; ++index) { 082 if (keys[index] != null) { 083 return; 084 } 085 } 086 } 087 } 088 089 private class EntrySet extends AbstractSet<Entry<K, V>> { 090 @Override 091 public boolean add(Entry<K, V> entry) { 092 boolean result = !HashMap.this.containsKey(entry.getKey()); 093 HashMap.this.put(entry.getKey(), entry.getValue()); 094 return result; 095 } 096 097 @Override 098 public boolean addAll(Collection<? extends Entry<K, V>> c) { 099 HashMap.this.ensureSizeFor(size() + c.size()); 100 return super.addAll(c); 101 } 102 103 @Override 104 public void clear() { 105 HashMap.this.clear(); 106 } 107 108 @Override 109 @SuppressWarnings("unchecked") 110 public boolean contains(Object o) { 111 if (!(o instanceof Entry)) { 112 return false; 113 } 114 Entry<K, V> entry = (Entry<K, V>) o; 115 V value = HashMap.this.get(entry.getKey()); 116 return HashMap.this.valueEquals(value, entry.getValue()); 117 } 118 119 @Override 120 public int hashCode() { 121 return HashMap.this.hashCode(); 122 } 123 124 @Override 125 public Iterator<java.util.Map.Entry<K, V>> iterator() { 126 return new EntryIterator(); 127 } 128 129 @Override 130 @SuppressWarnings("unchecked") 131 public boolean remove(Object o) { 132 if (!(o instanceof Entry)) { 133 return false; 134 } 135 Entry<K, V> entry = (Entry<K, V>) o; 136 int index = findKey(entry.getKey()); 137 if (index >= 0 && valueEquals(values[index], entry.getValue())) { 138 internalRemove(index); 139 return true; 140 } 141 return false; 142 } 143 144 @Override 145 public boolean removeAll(Collection<?> c) { 146 boolean didRemove = false; 147 for (Object o : c) { 148 didRemove |= remove(o); 149 } 150 return didRemove; 151 } 152 153 @Override 154 public int size() { 155 return HashMap.this.size; 156 } 157 } 158 159 private class HashEntry implements Entry<K, V> { 160 private final int index; 161 162 public HashEntry(int index) { 163 this.index = index; 164 } 165 166 @Override 167 @SuppressWarnings("unchecked") 168 public boolean equals(Object o) { 169 if (!(o instanceof Entry)) { 170 return false; 171 } 172 Entry<K, V> entry = (Entry<K, V>) o; 173 return keyEquals(getKey(), entry.getKey()) 174 && valueEquals(getValue(), entry.getValue()); 175 } 176 177 @SuppressWarnings("unchecked") 178 public K getKey() { 179 return (K) unmaskNullKey(keys[index]); 180 } 181 182 @SuppressWarnings("unchecked") 183 public V getValue() { 184 return (V) values[index]; 185 } 186 187 @Override 188 public int hashCode() { 189 return keyHashCode(getKey()) ^ valueHashCode(getValue()); 190 } 191 192 @SuppressWarnings("unchecked") 193 public V setValue(V value) { 194 V previous = (V) values[index]; 195 values[index] = value; 196 return previous; 197 } 198 199 @Override 200 public String toString() { 201 return getKey() + "=" + getValue(); 202 } 203 } 204 205 private class KeyIterator implements Iterator<K> { 206 private int index = 0; 207 private int last = -1; 208 209 { 210 advanceToItem(); 211 } 212 213 public boolean hasNext() { 214 return index < keys.length; 215 } 216 217 @SuppressWarnings("unchecked") 218 public K next() { 219 if (!hasNext()) { 220 throw new NoSuchElementException(); 221 } 222 last = index; 223 Object toReturn = unmaskNullKey(keys[index++]); 224 advanceToItem(); 225 return (K) toReturn; 226 } 227 228 public void remove() { 229 if (last < 0) { 230 throw new IllegalStateException(); 231 } 232 internalRemove(last); 233 if (keys[last] != null) { 234 index = last; 235 } 236 last = -1; 237 } 238 239 private void advanceToItem() { 240 for (; index < keys.length; ++index) { 241 if (keys[index] != null) { 242 return; 243 } 244 } 245 } 246 } 247 248 private class KeySet extends AbstractSet<K> { 249 @Override 250 public void clear() { 251 HashMap.this.clear(); 252 } 253 254 @Override 255 public boolean contains(Object o) { 256 return HashMap.this.containsKey(o); 257 } 258 259 @Override 260 public int hashCode() { 261 int result = 0; 262 for (int i = 0; i < keys.length; ++i) { 263 Object key = keys[i]; 264 if (key != null) { 265 result += keyHashCode(unmaskNullKey(key)); 266 } 267 } 268 return result; 269 } 270 271 @Override 272 public Iterator<K> iterator() { 273 return new KeyIterator(); 274 } 275 276 @Override 277 public boolean remove(Object o) { 278 int index = findKey(o); 279 if (index >= 0) { 280 internalRemove(index); 281 return true; 282 } 283 return false; 284 } 285 286 @Override 287 public boolean removeAll(Collection<?> c) { 288 boolean didRemove = false; 289 for (Object o : c) { 290 didRemove |= remove(o); 291 } 292 return didRemove; 293 } 294 295 @Override 296 public int size() { 297 return HashMap.this.size; 298 } 299 } 300 301 private class ValueIterator implements Iterator<V> { 302 private int index = 0; 303 private int last = -1; 304 305 { 306 advanceToItem(); 307 } 308 309 public boolean hasNext() { 310 return index < keys.length; 311 } 312 313 @SuppressWarnings("unchecked") 314 public V next() { 315 if (!hasNext()) { 316 throw new NoSuchElementException(); 317 } 318 last = index; 319 Object toReturn = values[index++]; 320 advanceToItem(); 321 return (V) toReturn; 322 } 323 324 public void remove() { 325 if (last < 0) { 326 throw new IllegalStateException(); 327 } 328 internalRemove(last); 329 if (keys[last] != null) { 330 index = last; 331 } 332 last = -1; 333 } 334 335 private void advanceToItem() { 336 for (; index < keys.length; ++index) { 337 if (keys[index] != null) { 338 return; 339 } 340 } 341 } 342 } 343 344 private class Values extends AbstractCollection<V> { 345 @Override 346 public void clear() { 347 HashMap.this.clear(); 348 } 349 350 @Override 351 public boolean contains(Object o) { 352 return HashMap.this.containsValue(o); 353 } 354 355 @Override 356 public int hashCode() { 357 int result = 0; 358 for (int i = 0; i < keys.length; ++i) { 359 if (keys[i] != null) { 360 result += valueHashCode(values[i]); 361 } 362 } 363 return result; 364 } 365 366 @Override 367 public Iterator<V> iterator() { 368 return new ValueIterator(); 369 } 370 371 @Override 372 public boolean remove(Object o) { 373 if (o == null) { 374 for (int i = 0; i < keys.length; ++i) { 375 if (keys[i] != null && values[i] == null) { 376 internalRemove(i); 377 return true; 378 } 379 } 380 } else { 381 for (int i = 0; i < keys.length; ++i) { 382 if (valueEquals(values[i], o)) { 383 internalRemove(i); 384 return true; 385 } 386 } 387 } 388 return false; 389 } 390 391 @Override 392 public boolean removeAll(Collection<?> c) { 393 boolean didRemove = false; 394 for (Object o : c) { 395 didRemove |= remove(o); 396 } 397 return didRemove; 398 } 399 400 @Override 401 public int size() { 402 return HashMap.this.size; 403 } 404 } 405 406 private static final Object NULL_KEY = new Serializable() { 407 Object readResolve() { 408 return NULL_KEY; 409 } 410 }; 411 412 static Object maskNullKey(Object k) { 413 return (k == null) ? NULL_KEY : k; 414 } 415 416 static Object unmaskNullKey(Object k) { 417 return (k == NULL_KEY) ? null : k; 418 } 419 420 /** 421 * Backing store for all the keys; transient due to custom serialization. 422 * Default access to avoid synthetic accessors from inner classes. 423 */ 424 transient Object[] keys; 425 426 /** 427 * Number of pairs in this set; transient due to custom serialization. Default 428 * access to avoid synthetic accessors from inner classes. 429 */ 430 transient int size = 0; 431 432 /** 433 * Backing store for all the values; transient due to custom serialization. 434 * Default access to avoid synthetic accessors from inner classes. 435 */ 436 transient Object[] values; 437 438 public HashMap() { 439 initTable(INITIAL_TABLE_SIZE); 440 } 441 442 public HashMap(Map<? extends K, ? extends V> m) { 443 int newCapacity = INITIAL_TABLE_SIZE; 444 int expectedSize = m.size(); 445 while (newCapacity * 3 < expectedSize * 4) { 446 newCapacity <<= 1; 447 } 448 449 initTable(newCapacity); 450 internalPutAll(m); 451 } 452 453 public void clear() { 454 initTable(INITIAL_TABLE_SIZE); 455 size = 0; 456 } 457 458 public boolean containsKey(Object key) { 459 return findKey(key) >= 0; 460 } 461 462 public boolean containsValue(Object value) { 463 if (value == null) { 464 for (int i = 0; i < keys.length; ++i) { 465 if (keys[i] != null && values[i] == null) { 466 return true; 467 } 468 } 469 } else { 470 for (Object existing : values) { 471 if (valueEquals(existing, value)) { 472 return true; 473 } 474 } 475 } 476 return false; 477 } 478 479 public Set<Entry<K, V>> entrySet() { 480 return new EntrySet(); 481 } 482 483 @Override 484 @SuppressWarnings("unchecked") 485 public boolean equals(Object o) { 486 if (!(o instanceof Map)) { 487 return false; 488 } 489 Map<K, V> other = (Map<K, V>) o; 490 return entrySet().equals(other.entrySet()); 491 } 492 493 @SuppressWarnings("unchecked") 494 public V get(Object key) { 495 int index = findKey(key); 496 return (index < 0) ? null : (V) values[index]; 497 } 498 499 @Override 500 public int hashCode() { 501 int result = 0; 502 for (int i = 0; i < keys.length; ++i) { 503 Object key = keys[i]; 504 if (key != null) { 505 result += keyHashCode(unmaskNullKey(key)) ^ valueHashCode(values[i]); 506 } 507 } 508 return result; 509 } 510 511 public boolean isEmpty() { 512 return size == 0; 513 } 514 515 public Set<K> keySet() { 516 return new KeySet(); 517 } 518 519 @SuppressWarnings("unchecked") 520 public V put(K key, V value) { 521 ensureSizeFor(size + 1); 522 int index = findKeyOrEmpty(key); 523 if (keys[index] == null) { 524 ++size; 525 keys[index] = maskNullKey(key); 526 values[index] = value; 527 return null; 528 } else { 529 Object previousValue = values[index]; 530 values[index] = value; 531 return (V) previousValue; 532 } 533 } 534 535 public void putAll(Map<? extends K, ? extends V> m) { 536 ensureSizeFor(size + m.size()); 537 internalPutAll(m); 538 } 539 540 @SuppressWarnings("unchecked") 541 public V remove(Object key) { 542 int index = findKey(key); 543 if (index < 0) { 544 return null; 545 } 546 Object previousValue = values[index]; 547 internalRemove(index); 548 return (V) previousValue; 549 } 550 551 public int size() { 552 return size; 553 } 554 555 @Override 556 public String toString() { 557 if (size == 0) { 558 return "{}"; 559 } 560 StringBuilder buf = new StringBuilder(32 * size()); 561 buf.append('{'); 562 563 boolean needComma = false; 564 for (int i = 0; i < keys.length; ++i) { 565 Object key = keys[i]; 566 if (key != null) { 567 if (needComma) { 568 buf.append(',').append(' '); 569 } 570 key = unmaskNullKey(key); 571 Object value = values[i]; 572 buf.append(key == this ? "(this Map)" : key).append('=').append( 573 value == this ? "(this Map)" : value); 574 needComma = true; 575 } 576 } 577 buf.append('}'); 578 return buf.toString(); 579 } 580 581 public Collection<V> values() { 582 return new Values(); 583 } 584 585 /** 586 * Adapted from org.apache.commons.collections.map.AbstractHashedMap. 587 */ 588 @SuppressWarnings("unchecked") 589 protected void doReadObject(ObjectInputStream in) throws IOException, 590 ClassNotFoundException { 591 int capacity = in.readInt(); 592 initTable(capacity); 593 int items = in.readInt(); 594 for (int i = 0; i < items; i++) { 595 Object key = in.readObject(); 596 Object value = in.readObject(); 597 put((K) key, (V) value); 598 } 599 } 600 601 /** 602 * Adapted from org.apache.commons.collections.map.AbstractHashedMap. 603 */ 604 protected void doWriteObject(ObjectOutputStream out) throws IOException { 605 out.writeInt(keys.length); 606 out.writeInt(size); 607 for (int i = 0; i < keys.length; ++i) { 608 Object key = keys[i]; 609 if (key != null) { 610 out.writeObject(unmaskNullKey(key)); 611 out.writeObject(values[i]); 612 } 613 } 614 } 615 616 /** 617 * Returns whether two keys are equal for the purposes of this set. 618 */ 619 protected boolean keyEquals(Object a, Object b) { 620 return (a == null) ? (b == null) : a.equals(b); 621 } 622 623 /** 624 * Returns the hashCode for a key. 625 */ 626 protected int keyHashCode(Object k) { 627 return (k == null) ? 0 : k.hashCode(); 628 } 629 630 /** 631 * Returns whether two values are equal for the purposes of this set. 632 */ 633 protected boolean valueEquals(Object a, Object b) { 634 return (a == null) ? (b == null) : a.equals(b); 635 } 636 637 /** 638 * Returns the hashCode for a value. 639 */ 640 protected int valueHashCode(Object v) { 641 return (v == null) ? 0 : v.hashCode(); 642 } 643 644 /** 645 * Ensures the map is large enough to contain the specified number of entries. 646 * Default access to avoid synthetic accessors from inner classes. 647 */ 648 void ensureSizeFor(int expectedSize) { 649 if (keys.length * 3 >= expectedSize * 4) { 650 return; 651 } 652 653 int newCapacity = keys.length << 1; 654 while (newCapacity * 3 < expectedSize * 4) { 655 newCapacity <<= 1; 656 } 657 658 Object[] oldKeys = keys; 659 Object[] oldValues = values; 660 initTable(newCapacity); 661 for (int i = 0; i < oldKeys.length; ++i) { 662 Object k = oldKeys[i]; 663 if (k != null) { 664 int newIndex = getKeyIndex(unmaskNullKey(k)); 665 while (keys[newIndex] != null) { 666 if (++newIndex == keys.length) { 667 newIndex = 0; 668 } 669 } 670 keys[newIndex] = k; 671 values[newIndex] = oldValues[i]; 672 } 673 } 674 } 675 676 /** 677 * Returns the index in the key table at which a particular key resides, or -1 678 * if the key is not in the table. Default access to avoid synthetic accessors 679 * from inner classes. 680 */ 681 int findKey(Object k) { 682 int index = getKeyIndex(k); 683 while (true) { 684 Object existing = keys[index]; 685 if (existing == null) { 686 return -1; 687 } 688 if (keyEquals(k, unmaskNullKey(existing))) { 689 return index; 690 } 691 if (++index == keys.length) { 692 index = 0; 693 } 694 } 695 } 696 697 /** 698 * Returns the index in the key table at which a particular key resides, or 699 * the index of an empty slot in the table where this key should be inserted 700 * if it is not already in the table. Default access to avoid synthetic 701 * accessors from inner classes. 702 */ 703 int findKeyOrEmpty(Object k) { 704 int index = getKeyIndex(k); 705 while (true) { 706 Object existing = keys[index]; 707 if (existing == null) { 708 return index; 709 } 710 if (keyEquals(k, unmaskNullKey(existing))) { 711 return index; 712 } 713 if (++index == keys.length) { 714 index = 0; 715 } 716 } 717 } 718 719 /** 720 * Removes the entry at the specified index, and performs internal management 721 * to make sure we don't wind up with a hole in the table. Default access to 722 * avoid synthetic accessors from inner classes. 723 */ 724 void internalRemove(int index) { 725 keys[index] = null; 726 values[index] = null; 727 --size; 728 plugHole(index); 729 } 730 731 private int getKeyIndex(Object k) { 732 int h = keyHashCode(k); 733 // Copied from Apache's AbstractHashedMap; prevents power-of-two collisions. 734 h += ~(h << 9); 735 h ^= (h >>> 14); 736 h += (h << 4); 737 h ^= (h >>> 10); 738 // Power of two trick. 739 return h & (keys.length - 1); 740 } 741 742 private void initTable(int capacity) { 743 keys = new Object[capacity]; 744 values = new Object[capacity]; 745 } 746 747 private void internalPutAll(Map<? extends K, ? extends V> m) { 748 for (Entry<? extends K, ? extends V> entry : m.entrySet()) { 749 K key = entry.getKey(); 750 V value = entry.getValue(); 751 int index = findKeyOrEmpty(key); 752 if (keys[index] == null) { 753 ++size; 754 keys[index] = maskNullKey(key); 755 values[index] = value; 756 } else { 757 values[index] = value; 758 } 759 } 760 } 761 762 /** 763 * Tricky, we left a hole in the map, which we have to fill. The only way to 764 * do this is to search forwards through the map shuffling back values that 765 * match this index until we hit a null. 766 */ 767 private void plugHole(int hole) { 768 int index = hole + 1; 769 if (index == keys.length) { 770 index = 0; 771 } 772 while (keys[index] != null) { 773 int targetIndex = getKeyIndex(unmaskNullKey(keys[index])); 774 if (hole < index) { 775 /* 776 * "Normal" case, the index is past the hole and the "bad range" is from 777 * hole (exclusive) to index (inclusive). 778 */ 779 if (!(hole < targetIndex && targetIndex <= index)) { 780 // Plug it! 781 keys[hole] = keys[index]; 782 values[hole] = values[index]; 783 keys[index] = null; 784 values[index] = null; 785 hole = index; 786 } 787 } else { 788 /* 789 * "Wrapped" case, the index is before the hole (we've wrapped) and the 790 * "good range" is from index (exclusive) to hole (inclusive). 791 */ 792 if (index < targetIndex && targetIndex <= hole) { 793 // Plug it! 794 keys[hole] = keys[index]; 795 values[hole] = values[index]; 796 keys[index] = null; 797 values[index] = null; 798 hole = index; 799 } 800 } 801 if (++index == keys.length) { 802 index = 0; 803 } 804 } 805 } 806 807 private void readObject(ObjectInputStream in) throws IOException, 808 ClassNotFoundException { 809 in.defaultReadObject(); 810 doReadObject(in); 811 } 812 813 private void writeObject(ObjectOutputStream out) throws IOException { 814 out.defaultWriteObject(); 815 doWriteObject(out); 816 } 817}