📄 sequencedhashmap.java
字号:
} }; } // constants to define what the iterator should return on "next" private static final int KEY = 0; private static final int VALUE = 1; private static final int ENTRY = 2; private static final int REMOVED_MASK = 0x80000000; private class OrderedIterator implements Iterator { /** * Holds the type that should be returned from the iterator. The value * should be either {@link #KEY}, {@link #VALUE}, or {@link #ENTRY}. To * save a tiny bit of memory, this field is also used as a marker for when * remove has been called on the current object to prevent a second remove * on the same element. Essientially, if this value is negative (i.e. the * bit specified by {@link #REMOVED_MASK} is set), the current position * has been removed. If positive, remove can still be called. **/ private int returnType; /** * Holds the "current" position in the iterator. When pos.next is the * sentinel, we've reached the end of the list. **/ private Entry pos = sentinel; /** * Holds the expected modification count. If the actual modification * count of the map differs from this value, then a concurrent * modification has occurred. **/ private transient long expectedModCount = modCount; /** * Construct an iterator over the sequenced elements in the order in which * they were added. The {@link #next()} method returns the type specified * by <code>returnType</code> which must be either {@link #KEY}, {@link * #VALUE}, or {@link #ENTRY}. **/ public OrderedIterator(int returnType) { // Set the "removed" bit so that the iterator starts in a state where // "next" must be called before "remove" will succeed. this.returnType = returnType | REMOVED_MASK; } /** * Returns whether there is any additional elements in the iterator to be * returned. * * @return <code>true</code> if there are more elements left to be * returned from the iterator; <code>false</code> otherwise. **/ public boolean hasNext() { return pos.next != sentinel; } /** * Returns the next element from the iterator. * * @return the next element from the iterator. * * @throws NoSuchElementException if there are no more elements in the * iterator. * * @throws ConcurrentModificationException if a modification occurs in * the underlying map. **/ public Object next() { if (modCount != expectedModCount) { throw new ConcurrentModificationException(Messages.getMessage("seqHashMapConcurrentModificationException00")); } if (pos.next == sentinel) { throw new NoSuchElementException(Messages.getMessage("seqHashMapNoSuchElementException00")); } // clear the "removed" flag returnType = returnType & ~REMOVED_MASK; pos = pos.next; switch (returnType) { case KEY : return pos.getKey(); case VALUE : return pos.getValue(); case ENTRY : return pos; default : // should never happen throw new Error(Messages.getMessage("seqHashMapBadIteratorType01", new Integer(returnType).toString())); } } /** * Removes the last element returned from the {@link #next()} method from * the sequenced map. * * @throws IllegalStateException if there isn't a "last element" to be * removed. That is, if {@link #next()} has never been called, or if * {@link #remove()} was already called on the element. * * @throws ConcurrentModificationException if a modification occurs in * the underlying map. **/ public void remove() { if ((returnType & REMOVED_MASK) != 0) { throw new IllegalStateException(Messages.getMessage("seqHashMapIllegalStateException00")); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(Messages.getMessage("seqHashMapConcurrentModificationException00")); } SequencedHashMap.this.removeImpl(pos.getKey()); // update the expected mod count for the remove operation expectedModCount++; // set the removed flag returnType = returnType | REMOVED_MASK; } } // APIs maintained from previous version of SequencedHashMap for backwards // compatibility /** * Creates a shallow copy of this object, preserving the internal structure * by copying only references. The keys and values themselves are not * <code>clone()</code>'d. The cloned object maintains the same sequence. * * @return A clone of this instance. * * @throws CloneNotSupportedException if clone is not supported by a * subclass. */ public Object clone() throws CloneNotSupportedException { // yes, calling super.clone() silly since we're just blowing away all // the stuff that super might be doing anyway, but for motivations on // this, see: // http://www.javaworld.com/javaworld/jw-01-1999/jw-01-object.html SequencedHashMap map = (SequencedHashMap) super.clone(); // create new, empty sentinel map.sentinel = createSentinel(); // create a new, empty entry map // note: this does not preserve the initial capacity and load factor. map.entries = new HashMap(); // add all the mappings map.putAll(this); // Note: We cannot just clone the hashmap and sentinel because we must // duplicate our internal structures. Cloning those two will not clone all // the other entries they reference, and so the cloned hash map will not be // able to maintain internal consistency because there are two objects with // the same entries. See discussion in the Entry implementation on why we // cannot implement a clone of the Entry (and thus why we need to recreate // everything). return map; } /** * Returns the Map.Entry at the specified index * * @throws ArrayIndexOutOfBoundsException if the specified index is * <code>< 0</code> or <code>></code> the size of the map. **/ private Map.Entry getEntry(int index) { Entry pos = sentinel; if (index < 0) { throw new ArrayIndexOutOfBoundsException(Messages.getMessage("seqHashMapArrayIndexOutOfBoundsException01", new Integer(index).toString())); } // loop to one before the position int i = -1; while (i < (index - 1) && pos.next != sentinel) { i++; pos = pos.next; } // pos.next is the requested position // if sentinel is next, past end of list if (pos.next == sentinel) { throw new ArrayIndexOutOfBoundsException(Messages.getMessage("seqHashMapArrayIndexOutOfBoundsException02", new Integer(index).toString(), new Integer(i + 1).toString())); } return pos.next; } /** * Gets the key at the specified index. * * @param index the index to retrieve * @return the key at the specified index, or null * @throws ArrayIndexOutOfBoundsException if the <code>index</code> is * <code>< 0</code> or <code>></code> the size of the map. */ public Object get(int index) { return getEntry(index).getKey(); } /** * Gets the value at the specified index. * * @param index the index to retrieve * @return the value at the specified index, or null * @throws ArrayIndexOutOfBoundsException if the <code>index</code> is * <code>< 0</code> or <code>></code> the size of the map. */ public Object getValue(int index) { return getEntry(index).getValue(); } /** * Gets the index of the specified key. * * @param key the key to find the index of * @return the index, or -1 if not found */ public int indexOf(Object key) { Entry e = (Entry) entries.get(key); if (e == null) { return -1; } int pos = 0; while (e.prev != sentinel) { pos++; e = e.prev; } return pos; } /** * Gets an iterator over the keys. * * @return an iterator over the keys */ public Iterator iterator() { return keySet().iterator(); } /** * Gets the last index of the specified key. * * @param key the key to find the index of * @return the index, or -1 if not found */ public int lastIndexOf(Object key) { // keys in a map are guarunteed to be unique return indexOf(key); } /** * Returns a List view of the keys rather than a set view. The returned * list is unmodifiable. This is required because changes to the values of * the list (using {@link java.util.ListIterator#set(Object)}) will * effectively remove the value from the list and reinsert that value at * the end of the list, which is an unexpected side effect of changing the * value of a list. This occurs because changing the key, changes when the * mapping is added to the map and thus where it appears in the list. * * <p>An alternative to this method is to use {@link #keySet()} * * @see #keySet() * @return The ordered list of keys. */ public List sequence() { List l = new ArrayList(size()); Iterator iter = keySet().iterator(); while (iter.hasNext()) { l.add(iter.next()); } return Collections.unmodifiableList(l); } /** * Removes the element at the specified index. * * @param index The index of the object to remove. * @return The previous value coressponding the <code>key</code>, or * <code>null</code> if none existed. * * @throws ArrayIndexOutOfBoundsException if the <code>index</code> is * <code>< 0</code> or <code>></code> the size of the map. */ public Object remove(int index) { return remove(get(index)); } // per Externalizable.readExternal(ObjectInput) /** * Deserializes this map from the given stream. * * @param in the stream to deserialize from * @throws IOException if the stream raises it * @throws ClassNotFoundException if the stream raises it */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int size = in.readInt(); for (int i = 0; i < size; i++) { Object key = in.readObject(); Object value = in.readObject(); put(key, value); } } /** * Serializes this map to the given stream. * * @param out the stream to serialize to * @throws IOException if the stream raises it */ public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(size()); for (Entry pos = sentinel.next; pos != sentinel; pos = pos.next) { out.writeObject(pos.getKey()); out.writeObject(pos.getValue()); } } // add a serial version uid, so that if we change things in the future // without changing the format, we can still deserialize properly. private static final long serialVersionUID = 3380552487888102930L;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -