1. /*
  2. * Copyright 2002-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.collections;
  17. import java.io.Externalizable;
  18. import java.io.IOException;
  19. import java.io.ObjectInput;
  20. import java.io.ObjectOutput;
  21. import java.util.AbstractCollection;
  22. import java.util.AbstractSet;
  23. import java.util.ArrayList;
  24. import java.util.Collection;
  25. import java.util.ConcurrentModificationException;
  26. import java.util.HashMap;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import java.util.Map;
  30. import java.util.NoSuchElementException;
  31. import java.util.Set;
  32. import org.apache.commons.collections.list.UnmodifiableList;
  33. /**
  34. * A map of objects whose mapping entries are sequenced based on the order in
  35. * which they were added. This data structure has fast <i>O(1)</i> search
  36. * time, deletion time, and insertion time.
  37. * <p>
  38. * Although this map is sequenced, it cannot implement
  39. * {@link java.util.List} because of incompatible interface definitions.
  40. * The remove methods in List and Map have different return values
  41. * (see: {@link java.util.List#remove(Object)} and {@link java.util.Map#remove(Object)}).
  42. * <p>
  43. * This class is not thread safe. When a thread safe implementation is
  44. * required, use {@link java.util.Collections#synchronizedMap(Map)} as it is documented,
  45. * or use explicit synchronization controls.
  46. *
  47. * @deprecated Replaced by LinkedMap and ListOrderedMap in map subpackage. Due to be removed in v4.0.
  48. * @see org.apache.commons.collections.map.LinkedMap
  49. * @see org.apache.commons.collections.map.ListOrderedMap
  50. * @since Commons Collections 2.0
  51. * @version $Revision: 1.28 $ $Date: 2004/02/18 01:15:42 $
  52. *
  53. * @author Michael A. Smith
  54. * @author Daniel Rall
  55. * @author Henning P. Schmiedehausen
  56. * @author Stephen Colebourne
  57. */
  58. public class SequencedHashMap implements Map, Cloneable, Externalizable {
  59. /**
  60. * {@link java.util.Map.Entry} that doubles as a node in the linked list
  61. * of sequenced mappings.
  62. */
  63. private static class Entry implements Map.Entry, KeyValue {
  64. // Note: This class cannot easily be made clonable. While the actual
  65. // implementation of a clone would be simple, defining the semantics is
  66. // difficult. If a shallow clone is implemented, then entry.next.prev !=
  67. // entry, which is unintuitive and probably breaks all sorts of assumptions
  68. // in code that uses this implementation. If a deep clone is
  69. // implemented, then what happens when the linked list is cyclical (as is
  70. // the case with SequencedHashMap)? It's impossible to know in the clone
  71. // when to stop cloning, and thus you end up in a recursive loop,
  72. // continuously cloning the "next" in the list.
  73. private final Object key;
  74. private Object value;
  75. // package private to allow the SequencedHashMap to access and manipulate
  76. // them.
  77. Entry next = null;
  78. Entry prev = null;
  79. public Entry(Object key, Object value) {
  80. this.key = key;
  81. this.value = value;
  82. }
  83. // per Map.Entry.getKey()
  84. public Object getKey() {
  85. return this.key;
  86. }
  87. // per Map.Entry.getValue()
  88. public Object getValue() {
  89. return this.value;
  90. }
  91. // per Map.Entry.setValue()
  92. public Object setValue(Object value) {
  93. Object oldValue = this.value;
  94. this.value = value;
  95. return oldValue;
  96. }
  97. public int hashCode() {
  98. // implemented per api docs for Map.Entry.hashCode()
  99. return ((getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode()));
  100. }
  101. public boolean equals(Object obj) {
  102. if (obj == null)
  103. return false;
  104. if (obj == this)
  105. return true;
  106. if (!(obj instanceof Map.Entry))
  107. return false;
  108. Map.Entry other = (Map.Entry) obj;
  109. // implemented per api docs for Map.Entry.equals(Object)
  110. return (
  111. (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey()))
  112. && (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())));
  113. }
  114. public String toString() {
  115. return "[" + getKey() + "=" + getValue() + "]";
  116. }
  117. }
  118. /**
  119. * Construct an empty sentinel used to hold the head (sentinel.next) and the
  120. * tail (sentinel.prev) of the list. The sentinel has a <code>null</code>
  121. * key and value.
  122. */
  123. private static final Entry createSentinel() {
  124. Entry s = new Entry(null, null);
  125. s.prev = s;
  126. s.next = s;
  127. return s;
  128. }
  129. /**
  130. * Sentinel used to hold the head and tail of the list of entries.
  131. */
  132. private Entry sentinel;
  133. /**
  134. * Map of keys to entries
  135. */
  136. private HashMap entries;
  137. /**
  138. * Holds the number of modifications that have occurred to the map,
  139. * excluding modifications made through a collection view's iterator
  140. * (e.g. entrySet().iterator().remove()). This is used to create a
  141. * fail-fast behavior with the iterators.
  142. */
  143. private transient long modCount = 0;
  144. /**
  145. * Construct a new sequenced hash map with default initial size and load
  146. * factor.
  147. */
  148. public SequencedHashMap() {
  149. sentinel = createSentinel();
  150. entries = new HashMap();
  151. }
  152. /**
  153. * Construct a new sequenced hash map with the specified initial size and
  154. * default load factor.
  155. *
  156. * @param initialSize the initial size for the hash table
  157. *
  158. * @see HashMap#HashMap(int)
  159. */
  160. public SequencedHashMap(int initialSize) {
  161. sentinel = createSentinel();
  162. entries = new HashMap(initialSize);
  163. }
  164. /**
  165. * Construct a new sequenced hash map with the specified initial size and
  166. * load factor.
  167. *
  168. * @param initialSize the initial size for the hash table
  169. *
  170. * @param loadFactor the load factor for the hash table.
  171. *
  172. * @see HashMap#HashMap(int,float)
  173. */
  174. public SequencedHashMap(int initialSize, float loadFactor) {
  175. sentinel = createSentinel();
  176. entries = new HashMap(initialSize, loadFactor);
  177. }
  178. /**
  179. * Construct a new sequenced hash map and add all the elements in the
  180. * specified map. The order in which the mappings in the specified map are
  181. * added is defined by {@link #putAll(Map)}.
  182. */
  183. public SequencedHashMap(Map m) {
  184. this();
  185. putAll(m);
  186. }
  187. /**
  188. * Removes an internal entry from the linked list. This does not remove
  189. * it from the underlying map.
  190. */
  191. private void removeEntry(Entry entry) {
  192. entry.next.prev = entry.prev;
  193. entry.prev.next = entry.next;
  194. }
  195. /**
  196. * Inserts a new internal entry to the tail of the linked list. This does
  197. * not add the entry to the underlying map.
  198. */
  199. private void insertEntry(Entry entry) {
  200. entry.next = sentinel;
  201. entry.prev = sentinel.prev;
  202. sentinel.prev.next = entry;
  203. sentinel.prev = entry;
  204. }
  205. // per Map.size()
  206. /**
  207. * Implements {@link Map#size()}.
  208. */
  209. public int size() {
  210. // use the underlying Map's size since size is not maintained here.
  211. return entries.size();
  212. }
  213. /**
  214. * Implements {@link Map#isEmpty()}.
  215. */
  216. public boolean isEmpty() {
  217. // for quick check whether the map is entry, we can check the linked list
  218. // and see if there's anything in it.
  219. return sentinel.next == sentinel;
  220. }
  221. /**
  222. * Implements {@link Map#containsKey(Object)}.
  223. */
  224. public boolean containsKey(Object key) {
  225. // pass on to underlying map implementation
  226. return entries.containsKey(key);
  227. }
  228. /**
  229. * Implements {@link Map#containsValue(Object)}.
  230. */
  231. public boolean containsValue(Object value) {
  232. // unfortunately, we cannot just pass this call to the underlying map
  233. // because we are mapping keys to entries, not keys to values. The
  234. // underlying map doesn't have an efficient implementation anyway, so this
  235. // isn't a big deal.
  236. // do null comparison outside loop so we only need to do it once. This
  237. // provides a tighter, more efficient loop at the expense of slight
  238. // code duplication.
  239. if (value == null) {
  240. for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {
  241. if (pos.getValue() == null)
  242. return true;
  243. }
  244. } else {
  245. for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {
  246. if (value.equals(pos.getValue()))
  247. return true;
  248. }
  249. }
  250. return false;
  251. }
  252. /**
  253. * Implements {@link Map#get(Object)}.
  254. */
  255. public Object get(Object o) {
  256. // find entry for the specified key object
  257. Entry entry = (Entry) entries.get(o);
  258. if (entry == null)
  259. return null;
  260. return entry.getValue();
  261. }
  262. /**
  263. * Return the entry for the "oldest" mapping. That is, return the Map.Entry
  264. * for the key-value pair that was first put into the map when compared to
  265. * all the other pairings in the map. This behavior is equivalent to using
  266. * <code>entrySet().iterator().next()</code>, but this method provides an
  267. * optimized implementation.
  268. *
  269. * @return The first entry in the sequence, or <code>null</code> if the
  270. * map is empty.
  271. */
  272. public Map.Entry getFirst() {
  273. // sentinel.next points to the "first" element of the sequence -- the head
  274. // of the list, which is exactly the entry we need to return. We must test
  275. // for an empty list though because we don't want to return the sentinel!
  276. return (isEmpty()) ? null : sentinel.next;
  277. }
  278. /**
  279. * Return the key for the "oldest" mapping. That is, return the key for the
  280. * mapping that was first put into the map when compared to all the other
  281. * objects in the map. This behavior is equivalent to using
  282. * <code>getFirst().getKey()</code>, but this method provides a slightly
  283. * optimized implementation.
  284. *
  285. * @return The first key in the sequence, or <code>null</code> if the
  286. * map is empty.
  287. */
  288. public Object getFirstKey() {
  289. // sentinel.next points to the "first" element of the sequence -- the head
  290. // of the list -- and the requisite key is returned from it. An empty list
  291. // does not need to be tested. In cases where the list is empty,
  292. // sentinel.next will point to the sentinel itself which has a null key,
  293. // which is exactly what we would want to return if the list is empty (a
  294. // nice convenient way to avoid test for an empty list)
  295. return sentinel.next.getKey();
  296. }
  297. /**
  298. * Return the value for the "oldest" mapping. That is, return the value for
  299. * the mapping that was first put into the map when compared to all the
  300. * other objects in the map. This behavior is equivalent to using
  301. * <code>getFirst().getValue()</code>, but this method provides a slightly
  302. * optimized implementation.
  303. *
  304. * @return The first value in the sequence, or <code>null</code> if the
  305. * map is empty.
  306. */
  307. public Object getFirstValue() {
  308. // sentinel.next points to the "first" element of the sequence -- the head
  309. // of the list -- and the requisite value is returned from it. An empty
  310. // list does not need to be tested. In cases where the list is empty,
  311. // sentinel.next will point to the sentinel itself which has a null value,
  312. // which is exactly what we would want to return if the list is empty (a
  313. // nice convenient way to avoid test for an empty list)
  314. return sentinel.next.getValue();
  315. }
  316. /**
  317. * Return the entry for the "newest" mapping. That is, return the Map.Entry
  318. * for the key-value pair that was first put into the map when compared to
  319. * all the other pairings in the map. The behavior is equivalent to:
  320. *
  321. * <pre>
  322. * Object obj = null;
  323. * Iterator iter = entrySet().iterator();
  324. * while(iter.hasNext()) {
  325. * obj = iter.next();
  326. * }
  327. * return (Map.Entry)obj;
  328. * </pre>
  329. *
  330. * However, the implementation of this method ensures an O(1) lookup of the
  331. * last key rather than O(n).
  332. *
  333. * @return The last entry in the sequence, or <code>null</code> if the map
  334. * is empty.
  335. */
  336. public Map.Entry getLast() {
  337. // sentinel.prev points to the "last" element of the sequence -- the tail
  338. // of the list, which is exactly the entry we need to return. We must test
  339. // for an empty list though because we don't want to return the sentinel!
  340. return (isEmpty()) ? null : sentinel.prev;
  341. }
  342. /**
  343. * Return the key for the "newest" mapping. That is, return the key for the
  344. * mapping that was last put into the map when compared to all the other
  345. * objects in the map. This behavior is equivalent to using
  346. * <code>getLast().getKey()</code>, but this method provides a slightly
  347. * optimized implementation.
  348. *
  349. * @return The last key in the sequence, or <code>null</code> if the map is
  350. * empty.
  351. */
  352. public Object getLastKey() {
  353. // sentinel.prev points to the "last" element of the sequence -- the tail
  354. // of the list -- and the requisite key is returned from it. An empty list
  355. // does not need to be tested. In cases where the list is empty,
  356. // sentinel.prev will point to the sentinel itself which has a null key,
  357. // which is exactly what we would want to return if the list is empty (a
  358. // nice convenient way to avoid test for an empty list)
  359. return sentinel.prev.getKey();
  360. }
  361. /**
  362. * Return the value for the "newest" mapping. That is, return the value for
  363. * the mapping that was last put into the map when compared to all the other
  364. * objects in the map. This behavior is equivalent to using
  365. * <code>getLast().getValue()</code>, but this method provides a slightly
  366. * optimized implementation.
  367. *
  368. * @return The last value in the sequence, or <code>null</code> if the map
  369. * is empty.
  370. */
  371. public Object getLastValue() {
  372. // sentinel.prev points to the "last" element of the sequence -- the tail
  373. // of the list -- and the requisite value is returned from it. An empty
  374. // list does not need to be tested. In cases where the list is empty,
  375. // sentinel.prev will point to the sentinel itself which has a null value,
  376. // which is exactly what we would want to return if the list is empty (a
  377. // nice convenient way to avoid test for an empty list)
  378. return sentinel.prev.getValue();
  379. }
  380. /**
  381. * Implements {@link Map#put(Object, Object)}.
  382. */
  383. public Object put(Object key, Object value) {
  384. modCount++;
  385. Object oldValue = null;
  386. // lookup the entry for the specified key
  387. Entry e = (Entry) entries.get(key);
  388. // check to see if it already exists
  389. if (e != null) {
  390. // remove from list so the entry gets "moved" to the end of list
  391. removeEntry(e);
  392. // update value in map
  393. oldValue = e.setValue(value);
  394. // Note: We do not update the key here because its unnecessary. We only
  395. // do comparisons using equals(Object) and we know the specified key and
  396. // that in the map are equal in that sense. This may cause a problem if
  397. // someone does not implement their hashCode() and/or equals(Object)
  398. // method properly and then use it as a key in this map.
  399. } else {
  400. // add new entry
  401. e = new Entry(key, value);
  402. entries.put(key, e);
  403. }
  404. // assert(entry in map, but not list)
  405. // add to list
  406. insertEntry(e);
  407. return oldValue;
  408. }
  409. /**
  410. * Implements {@link Map#remove(Object)}.
  411. */
  412. public Object remove(Object key) {
  413. Entry e = removeImpl(key);
  414. return (e == null) ? null : e.getValue();
  415. }
  416. /**
  417. * Fully remove an entry from the map, returning the old entry or null if
  418. * there was no such entry with the specified key.
  419. */
  420. private Entry removeImpl(Object key) {
  421. Entry e = (Entry) entries.remove(key);
  422. if (e == null)
  423. return null;
  424. modCount++;
  425. removeEntry(e);
  426. return e;
  427. }
  428. /**
  429. * Adds all the mappings in the specified map to this map, replacing any
  430. * mappings that already exist (as per {@link Map#putAll(Map)}). The order
  431. * in which the entries are added is determined by the iterator returned
  432. * from {@link Map#entrySet()} for the specified map.
  433. *
  434. * @param t the mappings that should be added to this map.
  435. *
  436. * @throws NullPointerException if <code>t</code> is <code>null</code>
  437. */
  438. public void putAll(Map t) {
  439. Iterator iter = t.entrySet().iterator();
  440. while (iter.hasNext()) {
  441. Map.Entry entry = (Map.Entry) iter.next();
  442. put(entry.getKey(), entry.getValue());
  443. }
  444. }
  445. /**
  446. * Implements {@link Map#clear()}.
  447. */
  448. public void clear() {
  449. modCount++;
  450. // remove all from the underlying map
  451. entries.clear();
  452. // and the list
  453. sentinel.next = sentinel;
  454. sentinel.prev = sentinel;
  455. }
  456. /**
  457. * Implements {@link Map#equals(Object)}.
  458. */
  459. public boolean equals(Object obj) {
  460. if (obj == null)
  461. return false;
  462. if (obj == this)
  463. return true;
  464. if (!(obj instanceof Map))
  465. return false;
  466. return entrySet().equals(((Map) obj).entrySet());
  467. }
  468. /**
  469. * Implements {@link Map#hashCode()}.
  470. */
  471. public int hashCode() {
  472. return entrySet().hashCode();
  473. }
  474. /**
  475. * Provides a string representation of the entries within the map. The
  476. * format of the returned string may change with different releases, so this
  477. * method is suitable for debugging purposes only. If a specific format is
  478. * required, use {@link #entrySet()}.{@link Set#iterator() iterator()} and
  479. * iterate over the entries in the map formatting them as appropriate.
  480. */
  481. public String toString() {
  482. StringBuffer buf = new StringBuffer();
  483. buf.append('[');
  484. for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {
  485. buf.append(pos.getKey());
  486. buf.append('=');
  487. buf.append(pos.getValue());
  488. if (pos.next != sentinel) {
  489. buf.append(',');
  490. }
  491. }
  492. buf.append(']');
  493. return buf.toString();
  494. }
  495. /**
  496. * Implements {@link Map#keySet()}.
  497. */
  498. public Set keySet() {
  499. return new AbstractSet() {
  500. // required impls
  501. public Iterator iterator() {
  502. return new OrderedIterator(KEY);
  503. }
  504. public boolean remove(Object o) {
  505. Entry e = SequencedHashMap.this.removeImpl(o);
  506. return (e != null);
  507. }
  508. // more efficient impls than abstract set
  509. public void clear() {
  510. SequencedHashMap.this.clear();
  511. }
  512. public int size() {
  513. return SequencedHashMap.this.size();
  514. }
  515. public boolean isEmpty() {
  516. return SequencedHashMap.this.isEmpty();
  517. }
  518. public boolean contains(Object o) {
  519. return SequencedHashMap.this.containsKey(o);
  520. }
  521. };
  522. }
  523. /**
  524. * Implements {@link Map#values()}.
  525. */
  526. public Collection values() {
  527. return new AbstractCollection() {
  528. // required impl
  529. public Iterator iterator() {
  530. return new OrderedIterator(VALUE);
  531. }
  532. public boolean remove(Object value) {
  533. // do null comparison outside loop so we only need to do it once. This
  534. // provides a tighter, more efficient loop at the expense of slight
  535. // code duplication.
  536. if (value == null) {
  537. for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {
  538. if (pos.getValue() == null) {
  539. SequencedHashMap.this.removeImpl(pos.getKey());
  540. return true;
  541. }
  542. }
  543. } else {
  544. for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {
  545. if (value.equals(pos.getValue())) {
  546. SequencedHashMap.this.removeImpl(pos.getKey());
  547. return true;
  548. }
  549. }
  550. }
  551. return false;
  552. }
  553. // more efficient impls than abstract collection
  554. public void clear() {
  555. SequencedHashMap.this.clear();
  556. }
  557. public int size() {
  558. return SequencedHashMap.this.size();
  559. }
  560. public boolean isEmpty() {
  561. return SequencedHashMap.this.isEmpty();
  562. }
  563. public boolean contains(Object o) {
  564. return SequencedHashMap.this.containsValue(o);
  565. }
  566. };
  567. }
  568. /**
  569. * Implements {@link Map#entrySet()}.
  570. */
  571. public Set entrySet() {
  572. return new AbstractSet() {
  573. // helper
  574. private Entry findEntry(Object o) {
  575. if (o == null)
  576. return null;
  577. if (!(o instanceof Map.Entry))
  578. return null;
  579. Map.Entry e = (Map.Entry) o;
  580. Entry entry = (Entry) entries.get(e.getKey());
  581. if (entry != null && entry.equals(e))
  582. return entry;
  583. else
  584. return null;
  585. }
  586. // required impl
  587. public Iterator iterator() {
  588. return new OrderedIterator(ENTRY);
  589. }
  590. public boolean remove(Object o) {
  591. Entry e = findEntry(o);
  592. if (e == null)
  593. return false;
  594. return SequencedHashMap.this.removeImpl(e.getKey()) != null;
  595. }
  596. // more efficient impls than abstract collection
  597. public void clear() {
  598. SequencedHashMap.this.clear();
  599. }
  600. public int size() {
  601. return SequencedHashMap.this.size();
  602. }
  603. public boolean isEmpty() {
  604. return SequencedHashMap.this.isEmpty();
  605. }
  606. public boolean contains(Object o) {
  607. return findEntry(o) != null;
  608. }
  609. };
  610. }
  611. // constants to define what the iterator should return on "next"
  612. private static final int KEY = 0;
  613. private static final int VALUE = 1;
  614. private static final int ENTRY = 2;
  615. private static final int REMOVED_MASK = 0x80000000;
  616. private class OrderedIterator implements Iterator {
  617. /**
  618. * Holds the type that should be returned from the iterator. The value
  619. * should be either {@link #KEY}, {@link #VALUE}, or {@link #ENTRY}. To
  620. * save a tiny bit of memory, this field is also used as a marker for when
  621. * remove has been called on the current object to prevent a second remove
  622. * on the same element. Essentially, if this value is negative (i.e. the
  623. * bit specified by {@link #REMOVED_MASK} is set), the current position
  624. * has been removed. If positive, remove can still be called.
  625. */
  626. private int returnType;
  627. /**
  628. * Holds the "current" position in the iterator. When pos.next is the
  629. * sentinel, we've reached the end of the list.
  630. */
  631. private Entry pos = sentinel;
  632. /**
  633. * Holds the expected modification count. If the actual modification
  634. * count of the map differs from this value, then a concurrent
  635. * modification has occurred.
  636. */
  637. private transient long expectedModCount = modCount;
  638. /**
  639. * Construct an iterator over the sequenced elements in the order in which
  640. * they were added. The {@link #next()} method returns the type specified
  641. * by <code>returnType</code> which must be either {@link #KEY}, {@link
  642. * #VALUE}, or {@link #ENTRY}.
  643. */
  644. public OrderedIterator(int returnType) {
  645. //// Since this is a private inner class, nothing else should have
  646. //// access to the constructor. Since we know the rest of the outer
  647. //// class uses the iterator correctly, we can leave of the following
  648. //// check:
  649. //if(returnType >= 0 && returnType <= 2) {
  650. // throw new IllegalArgumentException("Invalid iterator type");
  651. //}
  652. // Set the "removed" bit so that the iterator starts in a state where
  653. // "next" must be called before "remove" will succeed.
  654. this.returnType = returnType | REMOVED_MASK;
  655. }
  656. /**
  657. * Returns whether there is any additional elements in the iterator to be
  658. * returned.
  659. *
  660. * @return <code>true</code> if there are more elements left to be
  661. * returned from the iterator; <code>false</code> otherwise.
  662. */
  663. public boolean hasNext() {
  664. return pos.next != sentinel;
  665. }
  666. /**
  667. * Returns the next element from the iterator.
  668. *
  669. * @return the next element from the iterator.
  670. *
  671. * @throws NoSuchElementException if there are no more elements in the
  672. * iterator.
  673. *
  674. * @throws ConcurrentModificationException if a modification occurs in
  675. * the underlying map.
  676. */
  677. public Object next() {
  678. if (modCount != expectedModCount) {
  679. throw new ConcurrentModificationException();
  680. }
  681. if (pos.next == sentinel) {
  682. throw new NoSuchElementException();
  683. }
  684. // clear the "removed" flag
  685. returnType = returnType & ~REMOVED_MASK;
  686. pos = pos.next;
  687. switch (returnType) {
  688. case KEY :
  689. return pos.getKey();
  690. case VALUE :
  691. return pos.getValue();
  692. case ENTRY :
  693. return pos;
  694. default :
  695. // should never happen
  696. throw new Error("bad iterator type: " + returnType);
  697. }
  698. }
  699. /**
  700. * Removes the last element returned from the {@link #next()} method from
  701. * the sequenced map.
  702. *
  703. * @throws IllegalStateException if there isn't a "last element" to be
  704. * removed. That is, if {@link #next()} has never been called, or if
  705. * {@link #remove()} was already called on the element.
  706. *
  707. * @throws ConcurrentModificationException if a modification occurs in
  708. * the underlying map.
  709. */
  710. public void remove() {
  711. if ((returnType & REMOVED_MASK) != 0) {
  712. throw new IllegalStateException("remove() must follow next()");
  713. }
  714. if (modCount != expectedModCount) {
  715. throw new ConcurrentModificationException();
  716. }
  717. SequencedHashMap.this.removeImpl(pos.getKey());
  718. // update the expected mod count for the remove operation
  719. expectedModCount++;
  720. // set the removed flag
  721. returnType = returnType | REMOVED_MASK;
  722. }
  723. }
  724. // APIs maintained from previous version of SequencedHashMap for backwards
  725. // compatibility
  726. /**
  727. * Creates a shallow copy of this object, preserving the internal structure
  728. * by copying only references. The keys and values themselves are not
  729. * <code>clone()</code>'d. The cloned object maintains the same sequence.
  730. *
  731. * @return A clone of this instance.
  732. *
  733. * @throws CloneNotSupportedException if clone is not supported by a
  734. * subclass.
  735. */
  736. public Object clone() throws CloneNotSupportedException {
  737. // yes, calling super.clone() silly since we're just blowing away all
  738. // the stuff that super might be doing anyway, but for motivations on
  739. // this, see:
  740. // http://www.javaworld.com/javaworld/jw-01-1999/jw-01-object.html
  741. SequencedHashMap map = (SequencedHashMap) super.clone();
  742. // create new, empty sentinel
  743. map.sentinel = createSentinel();
  744. // create a new, empty entry map
  745. // note: this does not preserve the initial capacity and load factor.
  746. map.entries = new HashMap();
  747. // add all the mappings
  748. map.putAll(this);
  749. // Note: We cannot just clone the hashmap and sentinel because we must
  750. // duplicate our internal structures. Cloning those two will not clone all
  751. // the other entries they reference, and so the cloned hash map will not be
  752. // able to maintain internal consistency because there are two objects with
  753. // the same entries. See discussion in the Entry implementation on why we
  754. // cannot implement a clone of the Entry (and thus why we need to recreate
  755. // everything).
  756. return map;
  757. }
  758. /**
  759. * Returns the Map.Entry at the specified index
  760. *
  761. * @throws ArrayIndexOutOfBoundsException if the specified index is
  762. * <code>< 0</code> or <code>></code> the size of the map.
  763. */
  764. private Map.Entry getEntry(int index) {
  765. Entry pos = sentinel;
  766. if (index < 0) {
  767. throw new ArrayIndexOutOfBoundsException(index + " < 0");
  768. }
  769. // loop to one before the position
  770. int i = -1;
  771. while (i < (index - 1) && pos.next != sentinel) {
  772. i++;
  773. pos = pos.next;
  774. }
  775. // pos.next is the requested position
  776. // if sentinel is next, past end of list
  777. if (pos.next == sentinel) {
  778. throw new ArrayIndexOutOfBoundsException(index + " >= " + (i + 1));
  779. }
  780. return pos.next;
  781. }
  782. /**
  783. * Gets the key at the specified index.
  784. *
  785. * @param index the index to retrieve
  786. * @return the key at the specified index, or null
  787. * @throws ArrayIndexOutOfBoundsException if the <code>index</code> is
  788. * <code>< 0</code> or <code>></code> the size of the map.
  789. */
  790. public Object get(int index) {
  791. return getEntry(index).getKey();
  792. }
  793. /**
  794. * Gets the value at the specified index.
  795. *
  796. * @param index the index to retrieve
  797. * @return the value at the specified index, or null
  798. * @throws ArrayIndexOutOfBoundsException if the <code>index</code> is
  799. * <code>< 0</code> or <code>></code> the size of the map.
  800. */
  801. public Object getValue(int index) {
  802. return getEntry(index).getValue();
  803. }
  804. /**
  805. * Gets the index of the specified key.
  806. *
  807. * @param key the key to find the index of
  808. * @return the index, or -1 if not found
  809. */
  810. public int indexOf(Object key) {
  811. Entry e = (Entry) entries.get(key);
  812. if (e == null) {
  813. return -1;
  814. }
  815. int pos = 0;
  816. while (e.prev != sentinel) {
  817. pos++;
  818. e = e.prev;
  819. }
  820. return pos;
  821. }
  822. /**
  823. * Gets an iterator over the keys.
  824. *
  825. * @return an iterator over the keys
  826. */
  827. public Iterator iterator() {
  828. return keySet().iterator();
  829. }
  830. /**
  831. * Gets the last index of the specified key.
  832. *
  833. * @param key the key to find the index of
  834. * @return the index, or -1 if not found
  835. */
  836. public int lastIndexOf(Object key) {
  837. // keys in a map are guaranteed to be unique
  838. return indexOf(key);
  839. }
  840. /**
  841. * Returns a List view of the keys rather than a set view. The returned
  842. * list is unmodifiable. This is required because changes to the values of
  843. * the list (using {@link java.util.ListIterator#set(Object)}) will
  844. * effectively remove the value from the list and reinsert that value at
  845. * the end of the list, which is an unexpected side effect of changing the
  846. * value of a list. This occurs because changing the key, changes when the
  847. * mapping is added to the map and thus where it appears in the list.
  848. *
  849. * <p>An alternative to this method is to use {@link #keySet()}
  850. *
  851. * @see #keySet()
  852. * @return The ordered list of keys.
  853. */
  854. public List sequence() {
  855. List l = new ArrayList(size());
  856. Iterator iter = keySet().iterator();
  857. while (iter.hasNext()) {
  858. l.add(iter.next());
  859. }
  860. return UnmodifiableList.decorate(l);
  861. }
  862. /**
  863. * Removes the element at the specified index.
  864. *
  865. * @param index The index of the object to remove.
  866. * @return The previous value corresponding the <code>key</code>, or
  867. * <code>null</code> if none existed.
  868. *
  869. * @throws ArrayIndexOutOfBoundsException if the <code>index</code> is
  870. * <code>< 0</code> or <code>></code> the size of the map.
  871. */
  872. public Object remove(int index) {
  873. return remove(get(index));
  874. }
  875. // per Externalizable.readExternal(ObjectInput)
  876. /**
  877. * Deserializes this map from the given stream.
  878. *
  879. * @param in the stream to deserialize from
  880. * @throws IOException if the stream raises it
  881. * @throws ClassNotFoundException if the stream raises it
  882. */
  883. public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
  884. int size = in.readInt();
  885. for (int i = 0; i < size; i++) {
  886. Object key = in.readObject();
  887. Object value = in.readObject();
  888. put(key, value);
  889. }
  890. }
  891. /**
  892. * Serializes this map to the given stream.
  893. *
  894. * @param out the stream to serialize to
  895. * @throws IOException if the stream raises it
  896. */
  897. public void writeExternal(ObjectOutput out) throws IOException {
  898. out.writeInt(size());
  899. for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) {
  900. out.writeObject(pos.getKey());
  901. out.writeObject(pos.getValue());
  902. }
  903. }
  904. // add a serial version uid, so that if we change things in the future
  905. // without changing the format, we can still deserialize properly.
  906. private static final long serialVersionUID = 3380552487888102930L;
  907. }