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