1 16 package org.apache.commons.collections; 17 18 import java.io.Externalizable ; 19 import java.io.IOException ; 20 import java.io.ObjectInput ; 21 import java.io.ObjectOutput ; 22 import java.util.AbstractCollection ; 23 import java.util.AbstractSet ; 24 import java.util.ArrayList ; 25 import java.util.Collection ; 26 import java.util.ConcurrentModificationException ; 27 import java.util.HashMap ; 28 import java.util.Iterator ; 29 import java.util.List ; 30 import java.util.Map ; 31 import java.util.NoSuchElementException ; 32 import java.util.Set ; 33 34 import org.apache.commons.collections.list.UnmodifiableList; 35 36 61 public class SequencedHashMap implements Map , Cloneable , Externalizable { 62 63 67 private static class Entry implements Map.Entry , KeyValue { 68 78 private final Object key; 79 private Object value; 80 81 Entry next = null; 84 Entry prev = null; 85 86 public Entry(Object key, Object value) { 87 this.key = key; 88 this.value = value; 89 } 90 91 public Object getKey() { 93 return this.key; 94 } 95 96 public Object getValue() { 98 return this.value; 99 } 100 101 public Object setValue(Object value) { 103 Object oldValue = this.value; 104 this.value = value; 105 return oldValue; 106 } 107 108 public int hashCode() { 109 return ((getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode())); 111 } 112 113 public boolean equals(Object obj) { 114 if (obj == null) 115 return false; 116 if (obj == this) 117 return true; 118 if (!(obj instanceof Map.Entry )) 119 return false; 120 121 Map.Entry other = (Map.Entry ) obj; 122 123 return ( 125 (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) 126 && (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue()))); 127 } 128 public String toString() { 129 return "[" + getKey() + "=" + getValue() + "]"; 130 } 131 } 132 133 138 private static final Entry createSentinel() { 139 Entry s = new Entry(null, null); 140 s.prev = s; 141 s.next = s; 142 return s; 143 } 144 145 148 private Entry sentinel; 149 150 153 private HashMap entries; 154 155 161 private transient long modCount = 0; 162 163 167 public SequencedHashMap() { 168 sentinel = createSentinel(); 169 entries = new HashMap (); 170 } 171 172 180 public SequencedHashMap(int initialSize) { 181 sentinel = createSentinel(); 182 entries = new HashMap (initialSize); 183 } 184 185 195 public SequencedHashMap(int initialSize, float loadFactor) { 196 sentinel = createSentinel(); 197 entries = new HashMap (initialSize, loadFactor); 198 } 199 200 205 public SequencedHashMap(Map m) { 206 this(); 207 putAll(m); 208 } 209 210 214 private void removeEntry(Entry entry) { 215 entry.next.prev = entry.prev; 216 entry.prev.next = entry.next; 217 } 218 219 223 private void insertEntry(Entry entry) { 224 entry.next = sentinel; 225 entry.prev = sentinel.prev; 226 sentinel.prev.next = entry; 227 sentinel.prev = entry; 228 } 229 230 232 235 public int size() { 236 return entries.size(); 238 } 239 240 243 public boolean isEmpty() { 244 return sentinel.next == sentinel; 247 } 248 249 252 public boolean containsKey(Object key) { 253 return entries.containsKey(key); 255 } 256 257 260 public boolean containsValue(Object value) { 261 266 if (value == null) { 270 for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 271 if (pos.getValue() == null) 272 return true; 273 } 274 } else { 275 for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 276 if (value.equals(pos.getValue())) 277 return true; 278 } 279 } 280 return false; 281 } 282 283 286 public Object get(Object o) { 287 Entry entry = (Entry) entries.get(o); 289 if (entry == null) 290 return null; 291 292 return entry.getValue(); 293 } 294 295 305 public Map.Entry getFirst() { 306 return (isEmpty()) ? null : sentinel.next; 310 } 311 312 322 public Object getFirstKey() { 323 return sentinel.next.getKey(); 330 } 331 332 342 public Object getFirstValue() { 343 return sentinel.next.getValue(); 350 } 351 352 372 public Map.Entry getLast() { 373 return (isEmpty()) ? null : sentinel.prev; 377 } 378 379 389 public Object getLastKey() { 390 return sentinel.prev.getKey(); 397 } 398 399 409 public Object getLastValue() { 410 return sentinel.prev.getValue(); 417 } 418 419 422 public Object put(Object key, Object value) { 423 modCount++; 424 425 Object oldValue = null; 426 427 Entry e = (Entry) entries.get(key); 429 430 if (e != null) { 432 removeEntry(e); 434 435 oldValue = e.setValue(value); 437 438 } else { 444 e = new Entry(key, value); 446 entries.put(key, e); 447 } 448 450 insertEntry(e); 452 453 return oldValue; 454 } 455 456 459 public Object remove(Object key) { 460 Entry e = removeImpl(key); 461 return (e == null) ? null : e.getValue(); 462 } 463 464 468 private Entry removeImpl(Object key) { 469 Entry e = (Entry) entries.remove(key); 470 if (e == null) 471 return null; 472 modCount++; 473 removeEntry(e); 474 return e; 475 } 476 477 487 public void putAll(Map t) { 488 Iterator iter = t.entrySet().iterator(); 489 while (iter.hasNext()) { 490 Map.Entry entry = (Map.Entry ) iter.next(); 491 put(entry.getKey(), entry.getValue()); 492 } 493 } 494 495 498 public void clear() { 499 modCount++; 500 501 entries.clear(); 503 504 sentinel.next = sentinel; 506 sentinel.prev = sentinel; 507 } 508 509 512 public boolean equals(Object obj) { 513 if (obj == null) 514 return false; 515 if (obj == this) 516 return true; 517 518 if (!(obj instanceof Map )) 519 return false; 520 521 return entrySet().equals(((Map ) obj).entrySet()); 522 } 523 524 527 public int hashCode() { 528 return entrySet().hashCode(); 529 } 530 531 538 public String toString() { 539 StringBuffer buf = new StringBuffer (); 540 buf.append('['); 541 for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 542 buf.append(pos.getKey()); 543 buf.append('='); 544 buf.append(pos.getValue()); 545 if (pos.next != sentinel) { 546 buf.append(','); 547 } 548 } 549 buf.append(']'); 550 551 return buf.toString(); 552 } 553 554 557 public Set keySet() { 558 return new AbstractSet () { 559 560 public Iterator iterator() { 562 return new OrderedIterator(KEY); 563 } 564 public boolean remove(Object o) { 565 Entry e = SequencedHashMap.this.removeImpl(o); 566 return (e != null); 567 } 568 569 public void clear() { 571 SequencedHashMap.this.clear(); 572 } 573 public int size() { 574 return SequencedHashMap.this.size(); 575 } 576 public boolean isEmpty() { 577 return SequencedHashMap.this.isEmpty(); 578 } 579 public boolean contains(Object o) { 580 return SequencedHashMap.this.containsKey(o); 581 } 582 583 }; 584 } 585 586 589 public Collection values() { 590 return new AbstractCollection () { 591 public Iterator iterator() { 593 return new OrderedIterator(VALUE); 594 } 595 public boolean remove(Object value) { 596 if (value == null) { 600 for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 601 if (pos.getValue() == null) { 602 SequencedHashMap.this.removeImpl(pos.getKey()); 603 return true; 604 } 605 } 606 } else { 607 for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 608 if (value.equals(pos.getValue())) { 609 SequencedHashMap.this.removeImpl(pos.getKey()); 610 return true; 611 } 612 } 613 } 614 615 return false; 616 } 617 618 public void clear() { 620 SequencedHashMap.this.clear(); 621 } 622 public int size() { 623 return SequencedHashMap.this.size(); 624 } 625 public boolean isEmpty() { 626 return SequencedHashMap.this.isEmpty(); 627 } 628 public boolean contains(Object o) { 629 return SequencedHashMap.this.containsValue(o); 630 } 631 }; 632 } 633 634 637 public Set entrySet() { 638 return new AbstractSet () { 639 private Entry findEntry(Object o) { 641 if (o == null) 642 return null; 643 if (!(o instanceof Map.Entry )) 644 return null; 645 646 Map.Entry e = (Map.Entry ) o; 647 Entry entry = (Entry) entries.get(e.getKey()); 648 if (entry != null && entry.equals(e)) 649 return entry; 650 else 651 return null; 652 } 653 654 public Iterator iterator() { 656 return new OrderedIterator(ENTRY); 657 } 658 public boolean remove(Object o) { 659 Entry e = findEntry(o); 660 if (e == null) 661 return false; 662 663 return SequencedHashMap.this.removeImpl(e.getKey()) != null; 664 } 665 666 public void clear() { 668 SequencedHashMap.this.clear(); 669 } 670 public int size() { 671 return SequencedHashMap.this.size(); 672 } 673 public boolean isEmpty() { 674 return SequencedHashMap.this.isEmpty(); 675 } 676 public boolean contains(Object o) { 677 return findEntry(o) != null; 678 } 679 }; 680 } 681 682 private static final int KEY = 0; 684 private static final int VALUE = 1; 685 private static final int ENTRY = 2; 686 private static final int REMOVED_MASK = 0x80000000; 687 688 private class OrderedIterator implements Iterator { 689 698 private int returnType; 699 700 704 private Entry pos = sentinel; 705 706 711 private transient long expectedModCount = modCount; 712 713 719 public OrderedIterator(int returnType) { 720 728 this.returnType = returnType | REMOVED_MASK; 731 } 732 733 740 public boolean hasNext() { 741 return pos.next != sentinel; 742 } 743 744 755 public Object next() { 756 if (modCount != expectedModCount) { 757 throw new ConcurrentModificationException (); 758 } 759 if (pos.next == sentinel) { 760 throw new NoSuchElementException (); 761 } 762 763 returnType = returnType & ~REMOVED_MASK; 765 766 pos = pos.next; 767 switch (returnType) { 768 case KEY : 769 return pos.getKey(); 770 case VALUE : 771 return pos.getValue(); 772 case ENTRY : 773 return pos; 774 default : 775 throw new Error ("bad iterator type: " + returnType); 777 } 778 779 } 780 781 792 public void remove() { 793 if ((returnType & REMOVED_MASK) != 0) { 794 throw new IllegalStateException ("remove() must follow next()"); 795 } 796 if (modCount != expectedModCount) { 797 throw new ConcurrentModificationException (); 798 } 799 800 SequencedHashMap.this.removeImpl(pos.getKey()); 801 802 expectedModCount++; 804 805 returnType = returnType | REMOVED_MASK; 807 } 808 } 809 810 813 823 public Object clone() throws CloneNotSupportedException { 824 SequencedHashMap map = (SequencedHashMap) super.clone(); 829 830 map.sentinel = createSentinel(); 832 833 map.entries = new HashMap (); 836 837 map.putAll(this); 839 840 848 return map; 849 } 850 851 857 private Map.Entry getEntry(int index) { 858 Entry pos = sentinel; 859 860 if (index < 0) { 861 throw new ArrayIndexOutOfBoundsException (index + " < 0"); 862 } 863 864 int i = -1; 866 while (i < (index - 1) && pos.next != sentinel) { 867 i++; 868 pos = pos.next; 869 } 870 872 if (pos.next == sentinel) { 874 throw new ArrayIndexOutOfBoundsException (index + " >= " + (i + 1)); 875 } 876 877 return pos.next; 878 } 879 880 888 public Object get(int index) { 889 return getEntry(index).getKey(); 890 } 891 892 900 public Object getValue(int index) { 901 return getEntry(index).getValue(); 902 } 903 904 910 public int indexOf(Object key) { 911 Entry e = (Entry) entries.get(key); 912 if (e == null) { 913 return -1; 914 } 915 int pos = 0; 916 while (e.prev != sentinel) { 917 pos++; 918 e = e.prev; 919 } 920 return pos; 921 } 922 923 928 public Iterator iterator() { 929 return keySet().iterator(); 930 } 931 932 938 public int lastIndexOf(Object key) { 939 return indexOf(key); 941 } 942 943 957 public List sequence() { 958 List l = new ArrayList (size()); 959 Iterator iter = keySet().iterator(); 960 while (iter.hasNext()) { 961 l.add(iter.next()); 962 } 963 964 return UnmodifiableList.decorate(l); 965 } 966 967 977 public Object remove(int index) { 978 return remove(get(index)); 979 } 980 981 983 990 public void readExternal(ObjectInput in) throws IOException , ClassNotFoundException { 991 int size = in.readInt(); 992 for (int i = 0; i < size; i++) { 993 Object key = in.readObject(); 994 Object value = in.readObject(); 995 put(key, value); 996 } 997 } 998 999 1005 public void writeExternal(ObjectOutput out) throws IOException { 1006 out.writeInt(size()); 1007 for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { 1008 out.writeObject(pos.getKey()); 1009 out.writeObject(pos.getValue()); 1010 } 1011 } 1012 1013 private static final long serialVersionUID = 3380552487888102930L; 1016 1017} 1018 | Popular Tags |