001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of 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,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 *
019 */
020 package org.apache.directory.shared.ldap.util;
021
022
023 import java.io.Externalizable;
024 import java.io.IOException;
025 import java.io.ObjectInput;
026 import java.io.ObjectOutput;
027 import java.util.AbstractCollection;
028 import java.util.AbstractSet;
029 import java.util.ArrayList;
030 import java.util.Collection;
031 import java.util.Collections;
032 import java.util.ConcurrentModificationException;
033 import java.util.HashMap;
034 import java.util.Iterator;
035 import java.util.List;
036 import java.util.Map;
037 import java.util.NoSuchElementException;
038 import java.util.Set;
039
040
041 /**
042 * A map of objects whose mapping entries are sequenced based on the order in
043 * which they were added. This data structure has fast <i>O(1)</i> search time,
044 * deletion time, and insertion time.
045 * <p>
046 * Although this map is sequenced, it cannot implement {@link java.util.List}
047 * because of incompatible interface definitions. The remove methods in List and
048 * Map have different return values (see: {@link java.util.List#remove(Object)}
049 * and {@link java.util.Map#remove(Object)}).
050 * <p>
051 * This class is not thread safe. When a thread safe implementation is required,
052 * use {@link java.util.Collections#synchronizedMap(Map)} as it is documented,
053 * or use explicit synchronization controls.
054 *
055 * @since Commons Collections 2.0
056 * @version $Revision: 664290 $ $Date: 2008-06-07 08:28:06 +0200 (Sam, 07 jui 2008) $
057 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
058 */
059 public class SequencedHashMap implements Map, Cloneable, Externalizable
060 {
061
062 /**
063 * {@link java.util.Map.Entry} that doubles as a node in the linked list of
064 * sequenced mappings.
065 */
066 private static class Entry implements Map.Entry, KeyValue
067 {
068 // Note: This class cannot easily be made clonable. While the actual
069 // implementation of a clone would be simple, defining the semantics is
070 // difficult. If a shallow clone is implemented, then entry.next.prev !=
071 // entry, which is unintuitive and probably breaks all sorts of
072 // assumptions
073 // in code that uses this implementation. If a deep clone is
074 // implemented, then what happens when the linked list is cyclical (as
075 // is
076 // the case with SequencedHashMap)? It's impossible to know in the clone
077 // when to stop cloning, and thus you end up in a recursive loop,
078 // continuously cloning the "next" in the list.
079
080 private final Object key;
081
082 private Object value;
083
084 // package private to allow the SequencedHashMap to access and
085 // manipulate
086 // them.
087 Entry next = null;
088
089 Entry prev = null;
090
091
092 public Entry(Object key, Object value)
093 {
094 this.key = key;
095 this.value = value;
096 }
097
098
099 // per Map.Entry.getKey()
100 public Object getKey()
101 {
102 return this.key;
103 }
104
105
106 // per Map.Entry.getValue()
107 public Object getValue()
108 {
109 return this.value;
110 }
111
112
113 // per Map.Entry.setValue()
114 public Object setValue( Object value )
115 {
116 Object oldValue = this.value;
117 this.value = value;
118 return oldValue;
119 }
120
121
122 /**
123 * Compute the instance's hash code
124 * @return the instance's hash code
125 */
126 public int hashCode()
127 {
128 // implemented per api docs for Map.Entry.hashCode()
129 return ( ( getKey() == null ? 0 : getKey().hashCode() ) ^ ( getValue() == null ? 0 : getValue().hashCode() ) );
130 }
131
132
133 public boolean equals( Object obj )
134 {
135 if ( obj == null )
136 return false;
137 if ( obj == this )
138 return true;
139 if ( !( obj instanceof Map.Entry ) )
140 return false;
141
142 Map.Entry other = ( Map.Entry ) obj;
143
144 // implemented per api docs for Map.Entry.equals(Object)
145 return ( ( getKey() == null ? other.getKey() == null : getKey().equals( other.getKey() ) ) && ( getValue() == null ? other
146 .getValue() == null
147 : getValue().equals( other.getValue() ) ) );
148 }
149
150
151 public String toString()
152 {
153 return "[" + getKey() + "=" + getValue() + "]";
154 }
155 }
156
157
158 /**
159 * Construct an empty sentinel used to hold the head (sentinel.next) and the
160 * tail (sentinel.prev) of the list. The sentinel has a <code>null</code>
161 * key and value.
162 */
163 private static final Entry createSentinel()
164 {
165 Entry s = new Entry( null, null );
166 s.prev = s;
167 s.next = s;
168 return s;
169 }
170
171 /**
172 * Sentinel used to hold the head and tail of the list of entries.
173 */
174 private Entry sentinel;
175
176 /**
177 * Map of keys to entries
178 */
179 private HashMap entries;
180
181 /**
182 * Holds the number of modifications that have occurred to the map,
183 * excluding modifications made through a collection view's iterator (e.g.
184 * entrySet().iterator().remove()). This is used to create a fail-fast
185 * behavior with the iterators.
186 */
187 private transient long modCount = 0;
188
189
190 /**
191 * Construct a new sequenced hash map with default initial size and load
192 * factor.
193 */
194 public SequencedHashMap()
195 {
196 sentinel = createSentinel();
197 entries = new HashMap();
198 }
199
200
201 /**
202 * Construct a new sequenced hash map with the specified initial size and
203 * default load factor.
204 *
205 * @param initialSize
206 * the initial size for the hash table
207 * @see HashMap#HashMap(int)
208 */
209 public SequencedHashMap(int initialSize)
210 {
211 sentinel = createSentinel();
212 entries = new HashMap( initialSize );
213 }
214
215
216 /**
217 * Construct a new sequenced hash map with the specified initial size and
218 * load factor.
219 *
220 * @param initialSize
221 * the initial size for the hash table
222 * @param loadFactor
223 * the load factor for the hash table.
224 * @see HashMap#HashMap(int,float)
225 */
226 public SequencedHashMap(int initialSize, float loadFactor)
227 {
228 sentinel = createSentinel();
229 entries = new HashMap( initialSize, loadFactor );
230 }
231
232
233 /**
234 * Construct a new sequenced hash map and add all the elements in the
235 * specified map. The order in which the mappings in the specified map are
236 * added is defined by {@link #putAll(Map)}.
237 */
238 public SequencedHashMap(Map m)
239 {
240 this();
241 putAll( m );
242 }
243
244
245 /**
246 * Removes an internal entry from the linked list. This does not remove it
247 * from the underlying map.
248 */
249 private void removeEntry( Entry entry )
250 {
251 entry.next.prev = entry.prev;
252 entry.prev.next = entry.next;
253 }
254
255
256 /**
257 * Inserts a new internal entry to the tail of the linked list. This does
258 * not add the entry to the underlying map.
259 */
260 private void insertEntry( Entry entry )
261 {
262 entry.next = sentinel;
263 entry.prev = sentinel.prev;
264 sentinel.prev.next = entry;
265 sentinel.prev = entry;
266 }
267
268
269 // per Map.size()
270
271 /**
272 * Implements {@link Map#size()}.
273 */
274 public int size()
275 {
276 // use the underlying Map's size since size is not maintained here.
277 return entries.size();
278 }
279
280
281 /**
282 * Implements {@link Map#isEmpty()}.
283 */
284 public boolean isEmpty()
285 {
286 // for quick check whether the map is entry, we can check the linked
287 // list
288 // and see if there's anything in it.
289 return sentinel.next == sentinel;
290 }
291
292
293 /**
294 * Implements {@link Map#containsKey(Object)}.
295 */
296 public boolean containsKey( Object key )
297 {
298 // pass on to underlying map implementation
299 return entries.containsKey( key );
300 }
301
302
303 /**
304 * Implements {@link Map#containsValue(Object)}.
305 */
306 public boolean containsValue( Object value )
307 {
308 // unfortunately, we cannot just pass this call to the underlying map
309 // because we are mapping keys to entries, not keys to values. The
310 // underlying map doesn't have an efficient implementation anyway, so
311 // this
312 // isn't a big deal.
313
314 // do null comparison outside loop so we only need to do it once. This
315 // provides a tighter, more efficient loop at the expense of slight
316 // code duplication.
317 if ( value == null )
318 {
319 for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
320 {
321 if ( pos.getValue() == null )
322 return true;
323 }
324 }
325 else
326 {
327 for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
328 {
329 if ( value.equals( pos.getValue() ) )
330 return true;
331 }
332 }
333 return false;
334 }
335
336
337 /**
338 * Implements {@link Map#get(Object)}.
339 */
340 public Object get( Object o )
341 {
342 // find entry for the specified key object
343 Entry entry = ( Entry ) entries.get( o );
344 if ( entry == null )
345 return null;
346
347 return entry.getValue();
348 }
349
350
351 /**
352 * Return the entry for the "oldest" mapping. That is, return the Map.Entry
353 * for the key-value pair that was first put into the map when compared to
354 * all the other pairings in the map. This behavior is equivalent to using
355 * <code>entrySet().iterator().next()</code>, but this method provides an
356 * optimized implementation.
357 *
358 * @return The first entry in the sequence, or <code>null</code> if the
359 * map is empty.
360 */
361 public Map.Entry getFirst()
362 {
363 // sentinel.next points to the "first" element of the sequence -- the
364 // head
365 // of the list, which is exactly the entry we need to return. We must
366 // test
367 // for an empty list though because we don't want to return the
368 // sentinel!
369 return ( isEmpty() ) ? null : sentinel.next;
370 }
371
372
373 /**
374 * Return the key for the "oldest" mapping. That is, return the key for the
375 * mapping that was first put into the map when compared to all the other
376 * objects in the map. This behavior is equivalent to using
377 * <code>getFirst().getKey()</code>, but this method provides a slightly
378 * optimized implementation.
379 *
380 * @return The first key in the sequence, or <code>null</code> if the map
381 * is empty.
382 */
383 public Object getFirstKey()
384 {
385 // sentinel.next points to the "first" element of the sequence -- the
386 // head
387 // of the list -- and the requisite key is returned from it. An empty
388 // list
389 // does not need to be tested. In cases where the list is empty,
390 // sentinel.next will point to the sentinel itself which has a null key,
391 // which is exactly what we would want to return if the list is empty (a
392 // nice convenient way to avoid test for an empty list)
393 return sentinel.next.getKey();
394 }
395
396
397 /**
398 * Return the value for the "oldest" mapping. That is, return the value for
399 * the mapping that was first put into the map when compared to all the
400 * other objects in the map. This behavior is equivalent to using
401 * <code>getFirst().getValue()</code>, but this method provides a
402 * slightly optimized implementation.
403 *
404 * @return The first value in the sequence, or <code>null</code> if the
405 * map is empty.
406 */
407 public Object getFirstValue()
408 {
409 // sentinel.next points to the "first" element of the sequence -- the
410 // head
411 // of the list -- and the requisite value is returned from it. An empty
412 // list does not need to be tested. In cases where the list is empty,
413 // sentinel.next will point to the sentinel itself which has a null
414 // value,
415 // which is exactly what we would want to return if the list is empty (a
416 // nice convenient way to avoid test for an empty list)
417 return sentinel.next.getValue();
418 }
419
420
421 /**
422 * Return the entry for the "newest" mapping. That is, return the Map.Entry
423 * for the key-value pair that was first put into the map when compared to
424 * all the other pairings in the map. The behavior is equivalent to:
425 *
426 * <pre>
427 * Object obj = null;
428 * Iterator iter = entrySet().iterator();
429 * while ( iter.hasNext() )
430 * {
431 * obj = iter.next();
432 * }
433 * return ( Map.Entry ) obj;
434 * </pre>
435 *
436 * However, the implementation of this method ensures an O(1) lookup of the
437 * last key rather than O(n).
438 *
439 * @return The last entry in the sequence, or <code>null</code> if the map
440 * is empty.
441 */
442 public Map.Entry getLast()
443 {
444 // sentinel.prev points to the "last" element of the sequence -- the
445 // tail
446 // of the list, which is exactly the entry we need to return. We must
447 // test
448 // for an empty list though because we don't want to return the
449 // sentinel!
450 return ( isEmpty() ) ? null : sentinel.prev;
451 }
452
453
454 /**
455 * Return the key for the "newest" mapping. That is, return the key for the
456 * mapping that was last put into the map when compared to all the other
457 * objects in the map. This behavior is equivalent to using
458 * <code>getLast().getKey()</code>, but this method provides a slightly
459 * optimized implementation.
460 *
461 * @return The last key in the sequence, or <code>null</code> if the map
462 * is empty.
463 */
464 public Object getLastKey()
465 {
466 // sentinel.prev points to the "last" element of the sequence -- the
467 // tail
468 // of the list -- and the requisite key is returned from it. An empty
469 // list
470 // does not need to be tested. In cases where the list is empty,
471 // sentinel.prev will point to the sentinel itself which has a null key,
472 // which is exactly what we would want to return if the list is empty (a
473 // nice convenient way to avoid test for an empty list)
474 return sentinel.prev.getKey();
475 }
476
477
478 /**
479 * Return the value for the "newest" mapping. That is, return the value for
480 * the mapping that was last put into the map when compared to all the other
481 * objects in the map. This behavior is equivalent to using
482 * <code>getLast().getValue()</code>, but this method provides a slightly
483 * optimized implementation.
484 *
485 * @return The last value in the sequence, or <code>null</code> if the map
486 * is empty.
487 */
488 public Object getLastValue()
489 {
490 // sentinel.prev points to the "last" element of the sequence -- the
491 // tail
492 // of the list -- and the requisite value is returned from it. An empty
493 // list does not need to be tested. In cases where the list is empty,
494 // sentinel.prev will point to the sentinel itself which has a null
495 // value,
496 // which is exactly what we would want to return if the list is empty (a
497 // nice convenient way to avoid test for an empty list)
498 return sentinel.prev.getValue();
499 }
500
501
502 /**
503 * Implements {@link Map#put(Object, Object)}.
504 */
505 public Object put( Object key, Object value )
506 {
507 modCount++;
508
509 Object oldValue = null;
510
511 // lookup the entry for the specified key
512 Entry e = ( Entry ) entries.get( key );
513
514 // check to see if it already exists
515 if ( e != null )
516 {
517 // remove from list so the entry gets "moved" to the end of list
518 removeEntry( e );
519
520 // update value in map
521 oldValue = e.setValue( value );
522
523 // Note: We do not update the key here because its unnecessary. We
524 // only
525 // do comparisons using equals(Object) and we know the specified key
526 // and
527 // that in the map are equal in that sense. This may cause a problem
528 // if
529 // someone does not implement their hashCode() and/or equals(Object)
530 // method properly and then use it as a key in this map.
531 }
532 else
533 {
534 // add new entry
535 e = new Entry( key, value );
536 entries.put( key, e );
537 }
538 // assert(entry in map, but not list)
539
540 // add to list
541 insertEntry( e );
542
543 return oldValue;
544 }
545
546
547 /**
548 * Implements {@link Map#remove(Object)}.
549 */
550 public Object remove( Object key )
551 {
552 Entry e = removeImpl( key );
553 return ( e == null ) ? null : e.getValue();
554 }
555
556
557 /**
558 * Fully remove an entry from the map, returning the old entry or null if
559 * there was no such entry with the specified key.
560 */
561 private Entry removeImpl( Object key )
562 {
563 Entry e = ( Entry ) entries.remove( key );
564 if ( e == null )
565 return null;
566 modCount++;
567 removeEntry( e );
568 return e;
569 }
570
571
572 /**
573 * Adds all the mappings in the specified map to this map, replacing any
574 * mappings that already exist (as per {@link Map#putAll(Map)}). The order
575 * in which the entries are added is determined by the iterator returned
576 * from {@link Map#entrySet()} for the specified map.
577 *
578 * @param t
579 * the mappings that should be added to this map.
580 * @throws NullPointerException
581 * if <code>t</code> is <code>null</code>
582 */
583 public void putAll( Map t )
584 {
585 Iterator iter = t.entrySet().iterator();
586 while ( iter.hasNext() )
587 {
588 Map.Entry entry = ( Map.Entry ) iter.next();
589 put( entry.getKey(), entry.getValue() );
590 }
591 }
592
593
594 /**
595 * Implements {@link Map#clear()}.
596 */
597 public void clear()
598 {
599 modCount++;
600
601 // remove all from the underlying map
602 entries.clear();
603
604 // and the list
605 sentinel.next = sentinel;
606 sentinel.prev = sentinel;
607 }
608
609
610 /**
611 * Implements {@link Map#equals(Object)}.
612 */
613 public boolean equals( Object obj )
614 {
615 if ( obj == null )
616 return false;
617 if ( obj == this )
618 return true;
619
620 if ( !( obj instanceof Map ) )
621 return false;
622
623 return entrySet().equals( ( ( Map ) obj ).entrySet() );
624 }
625
626
627 /**
628 * Implements {@link Map#hashCode()}.
629 * @return the instance's hash code
630 */
631 public int hashCode()
632 {
633 return entrySet().hashCode();
634 }
635
636
637 /**
638 * Provides a string representation of the entries within the map. The
639 * format of the returned string may change with different releases, so this
640 * method is suitable for debugging purposes only. If a specific format is
641 * required, use {@link #entrySet()}.{@link Set#iterator() iterator()} and
642 * iterate over the entries in the map formatting them as appropriate.
643 */
644 public String toString()
645 {
646 StringBuffer buf = new StringBuffer();
647 buf.append( '[' );
648 for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
649 {
650 buf.append( pos.getKey() );
651 buf.append( '=' );
652 buf.append( pos.getValue() );
653 if ( pos.next != sentinel )
654 {
655 buf.append( ',' );
656 }
657 }
658 buf.append( ']' );
659
660 return buf.toString();
661 }
662
663
664 /**
665 * Implements {@link Map#keySet()}.
666 */
667 public Set keySet()
668 {
669 return new AbstractSet()
670 {
671
672 // required impls
673 public Iterator iterator()
674 {
675 return new OrderedIterator( KEY );
676 }
677
678
679 public boolean remove( Object o )
680 {
681 Entry e = SequencedHashMap.this.removeImpl( o );
682 return ( e != null );
683 }
684
685
686 // more efficient impls than abstract set
687 public void clear()
688 {
689 SequencedHashMap.this.clear();
690 }
691
692
693 public int size()
694 {
695 return SequencedHashMap.this.size();
696 }
697
698
699 public boolean isEmpty()
700 {
701 return SequencedHashMap.this.isEmpty();
702 }
703
704
705 public boolean contains( Object o )
706 {
707 return SequencedHashMap.this.containsKey( o );
708 }
709
710 };
711 }
712
713
714 /**
715 * Implements {@link Map#values()}.
716 */
717 public Collection values()
718 {
719 return new AbstractCollection()
720 {
721 // required impl
722 public Iterator iterator()
723 {
724 return new OrderedIterator( VALUE );
725 }
726
727
728 public boolean remove( Object value )
729 {
730 // do null comparison outside loop so we only need to do it
731 // once. This
732 // provides a tighter, more efficient loop at the expense of
733 // slight
734 // code duplication.
735 if ( value == null )
736 {
737 for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
738 {
739 if ( pos.getValue() == null )
740 {
741 SequencedHashMap.this.removeImpl( pos.getKey() );
742 return true;
743 }
744 }
745 }
746 else
747 {
748 for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
749 {
750 if ( value.equals( pos.getValue() ) )
751 {
752 SequencedHashMap.this.removeImpl( pos.getKey() );
753 return true;
754 }
755 }
756 }
757
758 return false;
759 }
760
761
762 // more efficient impls than abstract collection
763 public void clear()
764 {
765 SequencedHashMap.this.clear();
766 }
767
768
769 public int size()
770 {
771 return SequencedHashMap.this.size();
772 }
773
774
775 public boolean isEmpty()
776 {
777 return SequencedHashMap.this.isEmpty();
778 }
779
780
781 public boolean contains( Object o )
782 {
783 return SequencedHashMap.this.containsValue( o );
784 }
785 };
786 }
787
788
789 /**
790 * Implements {@link Map#entrySet()}.
791 */
792 public Set entrySet()
793 {
794 return new AbstractSet()
795 {
796 // helper
797 private Entry findEntry( Object o )
798 {
799 if ( o == null )
800 return null;
801 if ( !( o instanceof Map.Entry ) )
802 return null;
803
804 Map.Entry e = ( Map.Entry ) o;
805 Entry entry = ( Entry ) entries.get( e.getKey() );
806 if ( entry != null && entry.equals( e ) )
807 return entry;
808 else
809 return null;
810 }
811
812
813 // required impl
814 public Iterator iterator()
815 {
816 return new OrderedIterator( ENTRY );
817 }
818
819
820 public boolean remove( Object o )
821 {
822 Entry e = findEntry( o );
823 if ( e == null )
824 return false;
825
826 return SequencedHashMap.this.removeImpl( e.getKey() ) != null;
827 }
828
829
830 // more efficient impls than abstract collection
831 public void clear()
832 {
833 SequencedHashMap.this.clear();
834 }
835
836
837 public int size()
838 {
839 return SequencedHashMap.this.size();
840 }
841
842
843 public boolean isEmpty()
844 {
845 return SequencedHashMap.this.isEmpty();
846 }
847
848
849 public boolean contains( Object o )
850 {
851 return findEntry( o ) != null;
852 }
853 };
854 }
855
856 // constants to define what the iterator should return on "next"
857 private static final int KEY = 0;
858
859 private static final int VALUE = 1;
860
861 private static final int ENTRY = 2;
862
863 private static final int REMOVED_MASK = 0x80000000;
864
865 private class OrderedIterator implements Iterator
866 {
867 /**
868 * Holds the type that should be returned from the iterator. The value
869 * should be either KEY, VALUE, or ENTRY. To save a tiny bit of memory,
870 * this field is also used as a marker for when remove has been called
871 * on the current object to prevent a second remove on the same element.
872 * Essentially, if this value is negative (i.e. the bit specified by
873 * REMOVED_MASK is set), the current position has been removed. If
874 * positive, remove can still be called.
875 */
876 private int returnType;
877
878 /**
879 * Holds the "current" position in the iterator. When pos.next is the
880 * sentinel, we've reached the end of the list.
881 */
882 private Entry pos = sentinel;
883
884 /**
885 * Holds the expected modification count. If the actual modification
886 * count of the map differs from this value, then a concurrent
887 * modification has occurred.
888 */
889 private transient long expectedModCount = modCount;
890
891
892 /**
893 * Construct an iterator over the sequenced elements in the order in
894 * which they were added. The {@link #next()} method returns the type
895 * specified by <code>returnType</code> which must be either KEY,
896 * VALUE, or ENTRY.
897 */
898 public OrderedIterator(int returnType)
899 {
900 // // Since this is a private inner class, nothing else should have
901 // // access to the constructor. Since we know the rest of the outer
902 // // class uses the iterator correctly, we can leave of the
903 // following
904 // // check:
905 // if(returnType >= 0 && returnType <= 2) {
906 // throw new IllegalArgumentException("Invalid iterator type");
907 // }
908
909 // Set the "removed" bit so that the iterator starts in a state
910 // where
911 // "next" must be called before "remove" will succeed.
912 this.returnType = returnType | REMOVED_MASK;
913 }
914
915
916 /**
917 * Returns whether there is any additional elements in the iterator to
918 * be returned.
919 *
920 * @return <code>true</code> if there are more elements left to be
921 * returned from the iterator; <code>false</code> otherwise.
922 */
923 public boolean hasNext()
924 {
925 return pos.next != sentinel;
926 }
927
928
929 /**
930 * Returns the next element from the iterator.
931 *
932 * @return the next element from the iterator.
933 * @throws NoSuchElementException
934 * if there are no more elements in the iterator.
935 * @throws ConcurrentModificationException
936 * if a modification occurs in the underlying map.
937 */
938 public Object next()
939 {
940 if ( modCount != expectedModCount )
941 {
942 throw new ConcurrentModificationException();
943 }
944 if ( pos.next == sentinel )
945 {
946 throw new NoSuchElementException();
947 }
948
949 // clear the "removed" flag
950 returnType = returnType & ~REMOVED_MASK;
951
952 pos = pos.next;
953 switch ( returnType )
954 {
955 case KEY:
956 return pos.getKey();
957 case VALUE:
958 return pos.getValue();
959 case ENTRY:
960 return pos;
961 default:
962 // should never happen
963 throw new Error( "bad iterator type: " + returnType );
964 }
965
966 }
967
968
969 /**
970 * Removes the last element returned from the {@link #next()} method
971 * from the sequenced map.
972 *
973 * @throws IllegalStateException
974 * if there isn't a "last element" to be removed. That is,
975 * if {@link #next()} has never been called, or if
976 * {@link #remove()} was already called on the element.
977 * @throws ConcurrentModificationException
978 * if a modification occurs in the underlying map.
979 */
980 public void remove()
981 {
982 if ( ( returnType & REMOVED_MASK ) != 0 )
983 {
984 throw new IllegalStateException( "remove() must follow next()" );
985 }
986 if ( modCount != expectedModCount )
987 {
988 throw new ConcurrentModificationException();
989 }
990
991 SequencedHashMap.this.removeImpl( pos.getKey() );
992
993 // update the expected mod count for the remove operation
994 expectedModCount++;
995
996 // set the removed flag
997 returnType = returnType | REMOVED_MASK;
998 }
999 }
1000
1001
1002 // APIs maintained from previous version of SequencedHashMap for backwards
1003 // compatibility
1004
1005 /**
1006 * Creates a shallow copy of this object, preserving the internal structure
1007 * by copying only references. The keys and values themselves are not
1008 * <code>clone()</code>'d. The cloned object maintains the same sequence.
1009 *
1010 * @return A clone of this instance.
1011 * @throws CloneNotSupportedException
1012 * if clone is not supported by a subclass.
1013 */
1014 public Object clone() throws CloneNotSupportedException
1015 {
1016 // yes, calling super.clone() silly since we're just blowing away all
1017 // the stuff that super might be doing anyway, but for motivations on
1018 // this, see:
1019 // http://www.javaworld.com/javaworld/jw-01-1999/jw-01-object.html
1020 SequencedHashMap map = ( SequencedHashMap ) super.clone();
1021
1022 // create new, empty sentinel
1023 map.sentinel = createSentinel();
1024
1025 // create a new, empty entry map
1026 // note: this does not preserve the initial capacity and load factor.
1027 map.entries = new HashMap();
1028
1029 // add all the mappings
1030 map.putAll( this );
1031
1032 // Note: We cannot just clone the hashmap and sentinel because we must
1033 // duplicate our internal structures. Cloning those two will not clone
1034 // all
1035 // the other entries they reference, and so the cloned hash map will not
1036 // be
1037 // able to maintain internal consistency because there are two objects
1038 // with
1039 // the same entries. See discussion in the Entry implementation on why
1040 // we
1041 // cannot implement a clone of the Entry (and thus why we need to
1042 // recreate
1043 // everything).
1044
1045 return map;
1046 }
1047
1048
1049 /**
1050 * Returns the Map.Entry at the specified index
1051 *
1052 * @throws ArrayIndexOutOfBoundsException
1053 * if the specified index is <code>< 0</code> or
1054 * <code>></code> the size of the map.
1055 */
1056 private Map.Entry getEntry( int index )
1057 {
1058 Entry pos = sentinel;
1059
1060 if ( index < 0 )
1061 {
1062 throw new ArrayIndexOutOfBoundsException( index + " < 0" );
1063 }
1064
1065 // loop to one before the position
1066 int i = -1;
1067 while ( i < ( index - 1 ) && pos.next != sentinel )
1068 {
1069 i++;
1070 pos = pos.next;
1071 }
1072 // pos.next is the requested position
1073
1074 // if sentinel is next, past end of list
1075 if ( pos.next == sentinel )
1076 {
1077 throw new ArrayIndexOutOfBoundsException( index + " >= " + ( i + 1 ) );
1078 }
1079
1080 return pos.next;
1081 }
1082
1083
1084 /**
1085 * Gets the key at the specified index.
1086 *
1087 * @param index
1088 * the index to retrieve
1089 * @return the key at the specified index, or null
1090 * @throws ArrayIndexOutOfBoundsException
1091 * if the <code>index</code> is <code>< 0</code> or
1092 * <code>></code> the size of the map.
1093 */
1094 public Object get( int index )
1095 {
1096 return getEntry( index ).getKey();
1097 }
1098
1099
1100 /**
1101 * Gets the value at the specified index.
1102 *
1103 * @param index
1104 * the index to retrieve
1105 * @return the value at the specified index, or null
1106 * @throws ArrayIndexOutOfBoundsException
1107 * if the <code>index</code> is <code>< 0</code> or
1108 * <code>></code> the size of the map.
1109 */
1110 public Object getValue( int index )
1111 {
1112 return getEntry( index ).getValue();
1113 }
1114
1115
1116 /**
1117 * Gets the index of the specified key.
1118 *
1119 * @param key
1120 * the key to find the index of
1121 * @return the index, or -1 if not found
1122 */
1123 public int indexOf( Object key )
1124 {
1125 Entry e = ( Entry ) entries.get( key );
1126 if ( e == null )
1127 {
1128 return -1;
1129 }
1130 int pos = 0;
1131 while ( e.prev != sentinel )
1132 {
1133 pos++;
1134 e = e.prev;
1135 }
1136 return pos;
1137 }
1138
1139
1140 /**
1141 * Gets an iterator over the keys.
1142 *
1143 * @return an iterator over the keys
1144 */
1145 public Iterator iterator()
1146 {
1147 return keySet().iterator();
1148 }
1149
1150
1151 /**
1152 * Gets the last index of the specified key.
1153 *
1154 * @param key
1155 * the key to find the index of
1156 * @return the index, or -1 if not found
1157 */
1158 public int lastIndexOf( Object key )
1159 {
1160 // keys in a map are guaranteed to be unique
1161 return indexOf( key );
1162 }
1163
1164
1165 /**
1166 * Returns a List view of the keys rather than a set view. The returned list
1167 * is unmodifiable. This is required because changes to the values of the
1168 * list (using {@link java.util.ListIterator#set(Object)}) will effectively
1169 * remove the value from the list and reinsert that value at the end of the
1170 * list, which is an unexpected side effect of changing the value of a list.
1171 * This occurs because changing the key, changes when the mapping is added
1172 * to the map and thus where it appears in the list.
1173 * <p>
1174 * An alternative to this method is to use {@link #keySet()}
1175 *
1176 * @see #keySet()
1177 * @return The ordered list of keys.
1178 */
1179 public List sequence()
1180 {
1181 List l = new ArrayList( size() );
1182 Iterator iter = keySet().iterator();
1183 while ( iter.hasNext() )
1184 {
1185 l.add( iter.next() );
1186 }
1187
1188 return Collections.unmodifiableList( l );
1189 }
1190
1191
1192 /**
1193 * Removes the element at the specified index.
1194 *
1195 * @param index
1196 * The index of the object to remove.
1197 * @return The previous value corresponding the <code>key</code>, or
1198 * <code>null</code> if none existed.
1199 * @throws ArrayIndexOutOfBoundsException
1200 * if the <code>index</code> is <code>< 0</code> or
1201 * <code>></code> the size of the map.
1202 */
1203 public Object remove( int index )
1204 {
1205 return remove( get( index ) );
1206 }
1207
1208
1209 // per Externalizable.readExternal(ObjectInput)
1210
1211 /**
1212 * Deserializes this map from the given stream.
1213 *
1214 * @param in
1215 * the stream to deserialize from
1216 * @throws IOException
1217 * if the stream raises it
1218 * @throws ClassNotFoundException
1219 * if the stream raises it
1220 */
1221 public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException
1222 {
1223 int size = in.readInt();
1224 for ( int i = 0; i < size; i++ )
1225 {
1226 Object key = in.readObject();
1227 Object value = in.readObject();
1228 put( key, value );
1229 }
1230 }
1231
1232
1233 /**
1234 * Serializes this map to the given stream.
1235 *
1236 * @param out
1237 * the stream to serialize to
1238 * @throws IOException
1239 * if the stream raises it
1240 */
1241 public void writeExternal( ObjectOutput out ) throws IOException
1242 {
1243 out.writeInt( size() );
1244 for ( Entry pos = sentinel.next; pos != sentinel; pos = pos.next )
1245 {
1246 out.writeObject( pos.getKey() );
1247 out.writeObject( pos.getValue() );
1248 }
1249 }
1250
1251 // add a serial version uid, so that if we change things in the future
1252 // without changing the format, we can still deserialize properly.
1253 private static final long serialVersionUID = 3380552487888102930L;
1254
1255 }