| 1 19 package bak.pcj.map; 20 21 import bak.pcj.set.AbstractDoubleSet; 22 import bak.pcj.set.DoubleSet; 23 import bak.pcj.DoubleIterator; 24 import bak.pcj.hash.DoubleHashFunction; 25 import bak.pcj.hash.DefaultDoubleHashFunction; 26 import bak.pcj.util.Exceptions; 27 28 import java.util.Collection ; 29 import java.util.AbstractCollection ; 30 import java.util.Iterator ; 31 32 import java.io.Serializable ; 33 import java.io.IOException ; 34 import java.io.ObjectInputStream ; 35 import java.io.ObjectOutputStream ; 36 37 48 public class DoubleKeyChainedHashMap extends AbstractDoubleKeyMap implements DoubleKeyMap, Cloneable , Serializable { 49 50 51 private static final int GROWTH_POLICY_RELATIVE = 0; 52 53 54 private static final int GROWTH_POLICY_ABSOLUTE = 1; 55 56 61 private static final int DEFAULT_GROWTH_POLICY = GROWTH_POLICY_RELATIVE; 62 63 64 public static final double DEFAULT_GROWTH_FACTOR = 1.0; 65 66 67 public static final int DEFAULT_GROWTH_CHUNK = 10; 68 69 70 public static final int DEFAULT_CAPACITY = 11; 71 72 73 public static final double DEFAULT_LOAD_FACTOR = 0.75; 74 75 79 private DoubleHashFunction keyhash; 80 81 85 private int size; 86 87 88 private transient Entry[] data; 89 90 94 private int growthPolicy; 95 96 101 private double growthFactor; 102 103 108 private int growthChunk; 109 110 114 private double loadFactor; 115 116 120 private int expandAt; 121 122 123 private transient java.util.Set entries; 124 125 126 private transient DoubleSet keys; 127 128 129 private transient Collection values; 130 131 private DoubleKeyChainedHashMap(DoubleHashFunction keyhash, int capacity, int growthPolicy, double growthFactor, int growthChunk, double loadFactor) { 132 if (keyhash == null) 133 Exceptions.nullArgument("hash function"); 134 if (capacity < 0) 135 Exceptions.negativeArgument("capacity", String.valueOf(capacity)); 136 if (growthFactor < 0.0) 137 Exceptions.negativeArgument("growthFactor", String.valueOf(growthFactor)); 138 if (growthChunk < 0) 139 Exceptions.negativeArgument("growthChunk", String.valueOf(growthChunk)); 140 if (loadFactor <= 0.0) 141 Exceptions.negativeOrZeroArgument("loadFactor", String.valueOf(loadFactor)); 142 this.keyhash = keyhash; 143 data = new Entry[capacity]; 144 size = 0; 145 expandAt = (int)Math.round(loadFactor*capacity); 146 this.growthPolicy = growthPolicy; 147 this.growthFactor = growthFactor; 148 this.growthChunk = growthChunk; 149 this.loadFactor = loadFactor; 150 } 151 152 private DoubleKeyChainedHashMap(int capacity, int growthPolicy, double growthFactor, int growthChunk, double loadFactor) { 153 this(DefaultDoubleHashFunction.INSTANCE, capacity, growthPolicy, growthFactor, growthChunk, loadFactor); 154 } 155 156 160 public DoubleKeyChainedHashMap() { 161 this(DEFAULT_CAPACITY); 162 } 163 164 173 public DoubleKeyChainedHashMap(DoubleKeyMap map) { 174 this(); 175 putAll(map); 176 } 177 178 188 public DoubleKeyChainedHashMap(int capacity) { 189 this(capacity, DEFAULT_GROWTH_POLICY, DEFAULT_GROWTH_FACTOR, DEFAULT_GROWTH_CHUNK, DEFAULT_LOAD_FACTOR); 190 } 191 192 202 public DoubleKeyChainedHashMap(double loadFactor) { 203 this(DEFAULT_CAPACITY, DEFAULT_GROWTH_POLICY, DEFAULT_GROWTH_FACTOR, DEFAULT_GROWTH_CHUNK, loadFactor); 204 } 205 206 220 public DoubleKeyChainedHashMap(int capacity, double loadFactor) { 221 this(capacity, DEFAULT_GROWTH_POLICY, DEFAULT_GROWTH_FACTOR, DEFAULT_GROWTH_CHUNK, loadFactor); 222 } 223 224 247 public DoubleKeyChainedHashMap(int capacity, double loadFactor, double growthFactor) { 248 this(capacity, GROWTH_POLICY_RELATIVE, growthFactor, DEFAULT_GROWTH_CHUNK, loadFactor); 249 } 250 251 274 public DoubleKeyChainedHashMap(int capacity, double loadFactor, int growthChunk) { 275 this(capacity, GROWTH_POLICY_ABSOLUTE, DEFAULT_GROWTH_FACTOR, growthChunk, loadFactor); 276 } 277 278 282 292 public DoubleKeyChainedHashMap(DoubleHashFunction keyhash) { 293 this(keyhash, DEFAULT_CAPACITY, DEFAULT_GROWTH_POLICY, DEFAULT_GROWTH_FACTOR, DEFAULT_GROWTH_CHUNK, DEFAULT_LOAD_FACTOR); 294 } 295 296 312 public DoubleKeyChainedHashMap(DoubleHashFunction keyhash, int capacity) { 313 this(keyhash, capacity, DEFAULT_GROWTH_POLICY, DEFAULT_GROWTH_FACTOR, DEFAULT_GROWTH_CHUNK, DEFAULT_LOAD_FACTOR); 314 } 315 316 332 public DoubleKeyChainedHashMap(DoubleHashFunction keyhash, double loadFactor) { 333 this(keyhash, DEFAULT_CAPACITY, DEFAULT_GROWTH_POLICY, DEFAULT_GROWTH_FACTOR, DEFAULT_GROWTH_CHUNK, loadFactor); 334 } 335 336 356 public DoubleKeyChainedHashMap(DoubleHashFunction keyhash, int capacity, double loadFactor) { 357 this(keyhash, capacity, DEFAULT_GROWTH_POLICY, DEFAULT_GROWTH_FACTOR, DEFAULT_GROWTH_CHUNK, loadFactor); 358 } 359 360 389 public DoubleKeyChainedHashMap(DoubleHashFunction keyhash, int capacity, double loadFactor, double growthFactor) { 390 this(keyhash, capacity, GROWTH_POLICY_RELATIVE, growthFactor, DEFAULT_GROWTH_CHUNK, loadFactor); 391 } 392 393 422 public DoubleKeyChainedHashMap(DoubleHashFunction keyhash, int capacity, double loadFactor, int growthChunk) { 423 this(keyhash, capacity, GROWTH_POLICY_ABSOLUTE, DEFAULT_GROWTH_FACTOR, growthChunk, loadFactor); 424 } 425 426 430 private void ensureCapacity(int elements) { 431 if (elements >= expandAt) { 432 int newcapacity; 433 if (growthPolicy == GROWTH_POLICY_RELATIVE) 434 newcapacity = (int)(data.length * (1.0 + growthFactor)); 435 else 436 newcapacity = data.length + growthChunk; 437 if (newcapacity*loadFactor < elements) 438 newcapacity = (int)Math.round(((double)elements/loadFactor)); 439 newcapacity = bak.pcj.hash.Primes.nextPrime(newcapacity); 440 expandAt = (int)Math.round(loadFactor*newcapacity); 441 442 Entry[] newdata = new Entry[newcapacity]; 443 444 for (int i = 0; i < data.length; i++) { 446 Entry e = data[i]; 447 while (e != null) { 448 int index = Math.abs(keyhash.hash(e.key)) % newdata.length; 449 Entry next = e.next; 450 e.next = newdata[index]; 451 newdata[index] = e; 452 e = next; 453 } 454 } 455 456 data = newdata; 457 } 458 } 459 460 private Entry addList(Entry list, Entry v) { 461 v.next = list; 462 return v; 463 } 464 465 private Entry removeList(Entry list, Entry e) { 466 if (list == e) { 467 list = e.next; 468 e.next = null; 469 return list; 470 } 471 Entry listStart = list; 472 while (list.next != e) 473 list = list.next; 474 list.next = e.next; 475 e.next = null; 476 return listStart; 477 } 478 479 private Entry searchList(Entry list, double key) { 480 while (list != null) { 481 if (list.key == key) 482 return list; 483 list = list.next; 484 } 485 return null; 486 } 487 488 private Entry getEntry(double key) { 489 int index = Math.abs(keyhash.hash(key)) % data.length; 490 return searchList(data[index], key); 491 } 492 493 497 public DoubleSet keySet() { 498 if (keys == null) 499 keys = new KeySet(); 500 return keys; 501 } 502 503 public Object put(double key, Object value) { 504 Object result; 505 int index = Math.abs(keyhash.hash(key)) % data.length; 506 Entry e = searchList(data[index], key); 507 if (e == null) { 508 result = null; 509 e = new Entry(key, value); 510 e.next = data[index]; 511 data[index] = e; 512 ensureCapacity(size+1); 515 size++; 516 } else { 517 result = e.value; 518 e.value = value; 519 } 520 return result; 521 } 522 523 public Collection values() { 524 if (values == null) 525 values = new ValueCollection(); 526 return values; 527 } 528 529 536 public Object clone() { 537 try { 538 DoubleKeyChainedHashMap c = (DoubleKeyChainedHashMap)super.clone(); 539 c.data = new Entry[data.length]; 540 for (int i = 0; i < data.length; i++) 541 c.data[i] = cloneList(data[i]); 542 c.values = null; 544 c.keys = null; 545 return c; 546 } catch (CloneNotSupportedException e) { 547 Exceptions.cloning(); return null; 548 } 549 } 550 551 private Entry cloneList(Entry e) { 552 if (e == null) 553 return null; 554 Entry ne = new Entry(e.getKey(), e.getValue()); 555 ne.next = cloneList(e.next); 556 return ne; 557 } 558 559 private static class Entry { 560 double key; 561 Object value; 562 Entry next; 563 564 Entry(double key, Object value) { 565 this.key = key; 566 this.value = value; 567 } 568 569 public double getKey() 570 { return key; } 571 572 public Object getValue() 573 { return value; } 574 575 public boolean equals(Object obj) { 576 if (!(obj instanceof Entry)) 577 return false; 578 Entry e = (Entry)obj; 579 Object eval = e.getValue(); 580 if (eval == null) 581 return e.getKey() == key && value == null; 582 return e.getKey() == key && e.getValue().equals(value); 583 } 584 } 585 586 587 public DoubleKeyMapIterator entries() { 588 return new DoubleKeyMapIterator() { 589 Entry currEntry = null; 590 int nextList = nextList(0); 591 Entry nextEntry = nextList == -1 ? null : data[nextList]; 592 593 int nextList(int index) { 594 while (index < data.length && data[index] == null) 595 index++; 596 return index < data.length ? index : -1; 597 } 598 599 public boolean hasNext() { 600 return nextEntry != null; 601 } 602 603 public void next() { 604 if (nextEntry == null) 605 Exceptions.endOfIterator(); 606 currEntry = nextEntry; 607 608 nextEntry = nextEntry.next; 610 if (nextEntry == null) { 611 nextList = nextList(nextList+1); 612 if (nextList != -1) 613 nextEntry = data[nextList]; 614 } 615 } 616 617 public double getKey() { 618 if (currEntry == null) 619 Exceptions.noElementToGet(); 620 return currEntry.getKey(); 621 } 622 623 public Object getValue() { 624 if (currEntry == null) 625 Exceptions.noElementToGet(); 626 return currEntry.getValue(); 627 } 628 629 public void remove() { 630 if (currEntry == null) 631 Exceptions.noElementToRemove(); 632 DoubleKeyChainedHashMap.this.remove(currEntry.getKey()); 633 currEntry = null; 634 } 635 636 }; 637 } 638 639 private class KeySet extends AbstractDoubleSet { 640 641 public void clear() 642 { DoubleKeyChainedHashMap.this.clear(); } 643 644 public boolean contains(double v) { 645 return getEntry(v) != null; 646 } 647 648 public DoubleIterator iterator() { 649 return new DoubleIterator() { 650 Entry currEntry = null; 651 int nextList = nextList(0); 652 Entry nextEntry = nextList == -1 ? null : data[nextList]; 653 654 int nextList(int index) { 655 while (index < data.length && data[index] == null) 656 index++; 657 return index < data.length ? index : -1; 658 } 659 660 public boolean hasNext() { 661 return nextEntry != null; 662 } 663 664 public double next() { 665 if (nextEntry == null) 666 Exceptions.endOfIterator(); 667 currEntry = nextEntry; 668 669 nextEntry = nextEntry.next; 671 if (nextEntry == null) { 672 nextList = nextList(nextList+1); 673 if (nextList != -1) 674 nextEntry = data[nextList]; 675 } 676 return currEntry.key; 677 } 678 679 public void remove() { 680 if (currEntry == null) 681 Exceptions.noElementToRemove(); 682 DoubleKeyChainedHashMap.this.remove(currEntry.getKey()); 683 currEntry = null; 684 } 685 }; 686 } 687 688 public boolean remove(double v) { 689 boolean result = containsKey(v); 690 if (result) 691 DoubleKeyChainedHashMap.this.remove(v); 692 return result; 693 } 694 695 public int size() 696 { return size; } 697 698 } 699 700 701 private class ValueCollection extends AbstractCollection { 702 703 public void clear() 704 { DoubleKeyChainedHashMap.this.clear(); } 705 706 public boolean contains(Object v) { 707 return containsValue(v); 708 } 709 710 public Iterator iterator() { 711 return new Iterator() { 712 Entry currEntry = null; 713 int nextList = nextList(0); 714 Entry nextEntry = nextList == -1 ? null : data[nextList]; 715 716 int nextList(int index) { 717 while (index < data.length && data[index] == null) 718 index++; 719 return index < data.length ? index : -1; 720 } 721 722 public boolean hasNext() { 723 return nextEntry != null; 724 } 725 726 public Object next() { 727 if (nextEntry == null) 728 Exceptions.endOfIterator(); 729 currEntry = nextEntry; 730 731 nextEntry = nextEntry.next; 733 if (nextEntry == null) { 734 nextList = nextList(nextList+1); 735 if (nextList != -1) 736 nextEntry = data[nextList]; 737 } 738 return currEntry.value; 739 } 740 741 public void remove() { 742 if (currEntry == null) 743 Exceptions.noElementToRemove(); 744 DoubleKeyChainedHashMap.this.remove(currEntry.getKey()); 745 currEntry = null; 746 } 747 }; 748 } 749 750 public int size() 751 { return size; } 752 753 } 754 755 759 public void clear() { 760 java.util.Arrays.fill(data, null); 761 size = 0; 762 } 763 764 public boolean containsKey(double key) { 765 Entry e = getEntry(key); 766 return e != null; 767 } 768 769 public boolean containsValue(Object value) { 770 if (value == null) { 771 for (int i = 0; i < data.length; i++) { 772 Entry e = data[i]; 773 while (e != null) { 774 if (e.value == null) 775 return true; 776 e = e.next; 777 } 778 } 779 } else { 780 for (int i = 0; i < data.length; i++) { 781 Entry e = data[i]; 782 while (e != null) { 783 if (value.equals(e.value)) 784 return true; 785 e = e.next; 786 } 787 } 788 } 789 return false; 790 } 791 792 public Object get(double key) { 793 int index = Math.abs(keyhash.hash(key)) % data.length; 794 Entry e = searchList(data[index], key); 795 return e != null ? e.value : null; 796 } 797 798 public boolean isEmpty() 799 { return size == 0; } 800 801 public Object remove(double key) { 802 int index = Math.abs(keyhash.hash(key)) % data.length; 803 Entry e = searchList(data[index], key); 804 Object value; 805 if (e != null) { 806 data[index] = removeList(data[index], e); 808 value = e.value; 809 size--; 810 } else 811 value = null; 812 return value; 813 } 814 815 public int size() 816 { return size; } 817 818 822 829 private void writeObject(ObjectOutputStream s) throws IOException { 830 s.defaultWriteObject(); 831 s.writeInt(data.length); 832 DoubleKeyMapIterator i = entries(); 833 while (i.hasNext()) { 834 i.next(); 835 s.writeDouble(i.getKey()); 836 s.writeObject(i.getValue()); 837 } 838 } 839 840 843 private void readObject(ObjectInputStream s) throws IOException , ClassNotFoundException { 844 s.defaultReadObject(); 845 data = new Entry[s.readInt()]; 846 for (int i = 0; i < size; i++) { 847 double key = s.readDouble(); 848 Object value = s.readObject(); 849 int index = Math.abs(keyhash.hash(key)) % data.length; 850 Entry e = new Entry(key, value); 851 e.next = data[index]; 852 data[index] = e; 853 } 854 } 855 856 } | Popular Tags |