Refactor HashedMap and LinkedMap into Abstract superclasses

Adjust subclasses appropriately


git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@131419 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2003-12-07 23:59:13 +00:00
parent c32add2800
commit c9fc511b34
20 changed files with 2016 additions and 1612 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,575 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/AbstractLinkedMap.java,v 1.1 2003/12/07 23:59:13 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowledgement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgement may appear in the software itself,
* if and wherever such third-party acknowledgements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.collections.map;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.collections.MapIterator;
import org.apache.commons.collections.OrderedIterator;
import org.apache.commons.collections.OrderedMap;
import org.apache.commons.collections.OrderedMapIterator;
import org.apache.commons.collections.ResettableIterator;
/**
* An abstract implementation of a hash-based map that links entries to create an
* ordered map and which provides numerous points for subclasses to override.
* <p>
* This class implements all the features necessary for a subclass linked
* hash-based map. Key-value entries are stored in instances of the
* <code>LinkEntry</code> class which can be overridden and replaced.
* The iterators can similarly be replaced, without the need to replace the KeySet,
* EntrySet and Values view classes.
* <p>
* Overridable methods are provided to change the default hashing behaviour, and
* to change how entries are added to and removed from the map. Hopefully, all you
* need for unusual subclasses is here.
* <p>
* This implementation maintains order by original insertion, but subclasses
* may work differently. The <code>OrderedMap</code> interface is implemented
* to provide access to bidirectional iteration and extra convenience methods.
* <p>
* The <code>orderedMapIterator()</code> method provides direct access to a
* bidirectional iterator. The iterators from the other views can also be cast
* to <code>OrderedIterator</code> if required.
* <p>
* All the available iterators can be reset back to the start by casting to
* <code>ResettableIterator</code> and calling <code>reset()</code>.
* <p>
* The implementation is also designed to be subclassed, with lots of useful
* methods exposed.
*
* @since Commons Collections 3.0
* @version $Revision: 1.1 $ $Date: 2003/12/07 23:59:13 $
*
* @author java util LinkedHashMap
* @author Stephen Colebourne
*/
public class AbstractLinkedMap extends AbstractHashedMap implements OrderedMap {
/** Header in the linked list */
protected transient LinkEntry header;
/**
* Constructor only used in deserialization, do not use otherwise.
*/
protected AbstractLinkedMap() {
super();
}
/**
* Constructor which performs no validation on the passed in parameters.
*
* @param initialCapacity the initial capacity, must be a power of two
* @param loadFactor the load factor, must be > 0.0f and generally < 1.0f
* @param threshhold the threshold, must be sensible
*/
protected AbstractLinkedMap(int initialCapacity, float loadFactor, int threshhold) {
super(initialCapacity, loadFactor, threshhold);
}
/**
* Constructs a new, empty map with the specified initial capacity.
*
* @param initialCapacity the initial capacity
* @throws IllegalArgumentException if the initial capacity is less than one
*/
protected AbstractLinkedMap(int initialCapacity) {
super(initialCapacity);
}
/**
* Constructs a new, empty map with the specified initial capacity and
* load factor.
*
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is less than one
* @throws IllegalArgumentException if the load factor is less than zero
*/
protected AbstractLinkedMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
/**
* Constructor copying elements from another map.
*
* @param map the map to copy
* @throws NullPointerException if the map is null
*/
protected AbstractLinkedMap(Map map) {
super(map);
}
/**
* Initialise this subclass during construction.
*/
protected void init() {
header = new LinkEntry(null, -1, null, null);
header.before = header.after = header;
}
//-----------------------------------------------------------------------
/**
* Checks whether the map contains the specified value.
*
* @param value the value to search for
* @return true if the map contains the value
*/
public boolean containsValue(Object value) {
// override uses faster iterator
if (value == null) {
for (LinkEntry entry = header.after; entry != header; entry = entry.after) {
if (entry.getValue() == null) {
return true;
}
}
} else {
for (LinkEntry entry = header.after; entry != header; entry = entry.after) {
if (isEqualValue(value, entry.getValue())) {
return true;
}
}
}
return false;
}
/**
* Clears the map, resetting the size to zero and nullifying references
* to avoid garbage collection issues.
*/
public void clear() {
// override to reset the linked list
super.clear();
header.before = header.after = header;
}
//-----------------------------------------------------------------------
/**
* Gets the first key in the map, which is the most recently inserted.
*
* @return the most recently inserted key
*/
public Object firstKey() {
if (size == 0) {
throw new NoSuchElementException("Map is empty");
}
return header.after.getKey();
}
/**
* Gets the last key in the map, which is the first inserted.
*
* @return the eldest key
*/
public Object lastKey() {
if (size == 0) {
throw new NoSuchElementException("Map is empty");
}
return header.before.getKey();
}
/**
* Gets the next key in sequence.
*
* @param key the key to get after
* @return the next key
*/
public Object nextKey(Object key) {
LinkEntry entry = (LinkEntry) getEntry(key);
return (entry == null || entry.after == header ? null : entry.after.getKey());
}
/**
* Gets the previous key in sequence.
*
* @param key the key to get before
* @return the previous key
*/
public Object previousKey(Object key) {
LinkEntry entry = (LinkEntry) getEntry(key);
return (entry == null || entry.before == header ? null : entry.before.getKey());
}
//-----------------------------------------------------------------------
/**
* Adds an entry into this map, maintaining insertion order.
* <p>
* This implementation adds the entry to the data storage table and
* to the end of the linked list.
*
* @param entry the entry to add
* @param hashIndex the index into the data array to store at
*/
protected void addEntry(HashEntry entry, int hashIndex) {
LinkEntry link = (LinkEntry) entry;
link.after = header;
link.before = header.before;
header.before.after = link;
header.before = link;
data[hashIndex] = entry;
}
/**
* Creates an entry to store the data.
* <p>
* This implementation creates a new LinkEntry instance.
*
* @param next the next entry in sequence
* @param hashCode the hash code to use
* @param key the key to store
* @param value the value to store
* @return the newly created entry
*/
protected HashEntry createEntry(HashEntry next, int hashCode, Object key, Object value) {
return new LinkEntry(next, hashCode, key, value);
}
/**
* Removes an entry from the map and the linked list.
* <p>
* This implementation removes the entry from the linked list chain, then
* calls the superclass implementation.
*
* @param entry the entry to remove
* @param hashIndex the index into the data structure
* @param previous the previous entry in the chain
*/
protected void removeEntry(HashEntry entry, int hashIndex, HashEntry previous) {
LinkEntry link = (LinkEntry) entry;
link.before.after = link.after;
link.after.before = link.before;
link.after = null;
link.before = null;
super.removeEntry(entry, hashIndex, previous);
}
//-----------------------------------------------------------------------
/**
* Gets an iterator over the map.
* Changes made to the iterator affect this map.
* <p>
* A MapIterator returns the keys in the map. It also provides convenient
* methods to get the key and value, and set the value.
* It avoids the need to create an entrySet/keySet/values object.
* It also avoids creating the Mep Entry object.
*
* @return the map iterator
*/
public MapIterator mapIterator() {
if (size == 0) {
return IteratorUtils.EMPTY_ORDERED_MAP_ITERATOR;
}
return new LinkMapIterator(this);
}
/**
* Gets a bidirectional iterator over the map.
* Changes made to the iterator affect this map.
* <p>
* A MapIterator returns the keys in the map. It also provides convenient
* methods to get the key and value, and set the value.
* It avoids the need to create an entrySet/keySet/values object.
* It also avoids creating the Mep Entry object.
*
* @return the map iterator
*/
public OrderedMapIterator orderedMapIterator() {
if (size == 0) {
return IteratorUtils.EMPTY_ORDERED_MAP_ITERATOR;
}
return new LinkMapIterator(this);
}
/**
* MapIterator
*/
static class LinkMapIterator extends LinkIterator implements OrderedMapIterator {
LinkMapIterator(AbstractLinkedMap map) {
super(map);
}
public Object next() {
return super.nextEntry().getKey();
}
public Object previous() {
return super.previousEntry().getKey();
}
public Object getKey() {
HashEntry current = currentEntry();
if (current == null) {
throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
}
return current.getKey();
}
public Object getValue() {
HashEntry current = currentEntry();
if (current == null) {
throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
}
return current.getValue();
}
public Object setValue(Object value) {
HashEntry current = currentEntry();
if (current == null) {
throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
}
return current.setValue(value);
}
}
//-----------------------------------------------------------------------
/**
* Creates an entry set iterator.
* Subclasses can override this to return iterators with different properties.
*
* @return the entrySet iterator
*/
protected Iterator createEntrySetIterator() {
if (size() == 0) {
return IteratorUtils.EMPTY_ORDERED_ITERATOR;
}
return new EntrySetIterator(this);
}
/**
* EntrySetIterator and MapEntry
*/
static class EntrySetIterator extends LinkIterator {
EntrySetIterator(AbstractLinkedMap map) {
super(map);
}
public Object next() {
return super.nextEntry();
}
public Object previous() {
return super.previousEntry();
}
}
//-----------------------------------------------------------------------
/**
* Creates a key set iterator.
* Subclasses can override this to return iterators with different properties.
*
* @return the keySet iterator
*/
protected Iterator createKeySetIterator() {
if (size() == 0) {
return IteratorUtils.EMPTY_ORDERED_ITERATOR;
}
return new KeySetIterator(this);
}
/**
* KeySetIterator
*/
static class KeySetIterator extends EntrySetIterator {
KeySetIterator(AbstractLinkedMap map) {
super(map);
}
public Object next() {
return super.nextEntry().getKey();
}
public Object previous() {
return super.previousEntry().getKey();
}
}
//-----------------------------------------------------------------------
/**
* Creates a values iterator.
* Subclasses can override this to return iterators with different properties.
*
* @return the values iterator
*/
protected Iterator createValuesIterator() {
if (size() == 0) {
return IteratorUtils.EMPTY_ORDERED_ITERATOR;
}
return new ValuesIterator(this);
}
/**
* ValuesIterator
*/
static class ValuesIterator extends LinkIterator {
ValuesIterator(AbstractLinkedMap map) {
super(map);
}
public Object next() {
return super.nextEntry().getValue();
}
public Object previous() {
return super.previousEntry().getValue();
}
}
//-----------------------------------------------------------------------
/**
* LinkEntry
*/
protected static class LinkEntry extends HashEntry {
protected LinkEntry before;
protected LinkEntry after;
protected LinkEntry(HashEntry next, int hashCode, Object key, Object value) {
super(next, hashCode, key, value);
}
}
/**
* Base Iterator
*/
protected static abstract class LinkIterator
implements OrderedIterator, ResettableIterator {
protected final AbstractLinkedMap map;
protected LinkEntry current;
protected LinkEntry next;
protected int expectedModCount;
protected LinkIterator(AbstractLinkedMap map) {
super();
this.map = map;
this.next = map.header.after;
this.expectedModCount = map.modCount;
}
public boolean hasNext() {
return (next != map.header);
}
public boolean hasPrevious() {
return (next.before != map.header);
}
protected LinkEntry nextEntry() {
if (map.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (next == map.header) {
throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
}
current = next;
next = next.after;
return current;
}
protected LinkEntry previousEntry() {
if (map.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
LinkEntry previous = next.before;
if (previous == map.header) {
throw new NoSuchElementException(AbstractHashedMap.NO_PREVIOUS_ENTRY);
}
next = previous;
current = previous;
return current;
}
protected LinkEntry currentEntry() {
return current;
}
public void remove() {
if (current == null) {
throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
}
if (map.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
map.remove(current.getKey());
current = null;
expectedModCount = map.modCount;
}
public void reset() {
current = null;
next = map.header.after;
}
public String toString() {
if (current != null) {
return "Iterator[" + current.getKey() + "=" + current.getValue() + "]";
} else {
return "Iterator[]";
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/Flat3Map.java,v 1.7 2003/12/06 14:02:11 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/Flat3Map.java,v 1.8 2003/12/07 23:59:13 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -98,7 +98,7 @@ import org.apache.commons.collections.ResettableIterator;
* Do not use <code>Flat3Map</code> if the size is likely to grow beyond 3.
*
* @since Commons Collections 3.0
* @version $Revision: 1.7 $ $Date: 2003/12/06 14:02:11 $
* @version $Revision: 1.8 $ $Date: 2003/12/07 23:59:13 $
*
* @author Stephen Colebourne
*/
@ -609,7 +609,7 @@ public class Flat3Map implements IterableMap {
public Object next() {
if (hasNext() == false) {
throw new NoSuchElementException(HashedMap.NO_NEXT_ENTRY);
throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
}
iCanRemove = true;
iIndex++;
@ -618,7 +618,7 @@ public class Flat3Map implements IterableMap {
public void remove() {
if (iCanRemove == false) {
throw new IllegalStateException(HashedMap.REMOVE_INVALID);
throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
}
iFlatMap.remove(getKey());
iIndex--;
@ -627,7 +627,7 @@ public class Flat3Map implements IterableMap {
public Object getKey() {
if (iCanRemove == false) {
throw new IllegalStateException(HashedMap.GETKEY_INVALID);
throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
}
switch (iIndex) {
case 3:
@ -642,7 +642,7 @@ public class Flat3Map implements IterableMap {
public Object getValue() {
if (iCanRemove == false) {
throw new IllegalStateException(HashedMap.GETVALUE_INVALID);
throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
}
switch (iIndex) {
case 3:
@ -657,7 +657,7 @@ public class Flat3Map implements IterableMap {
public Object setValue(Object value) {
if (iCanRemove == false) {
throw new IllegalStateException(HashedMap.SETVALUE_INVALID);
throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
}
Object old = getValue();
switch (iIndex) {
@ -761,7 +761,7 @@ public class Flat3Map implements IterableMap {
public Object next() {
if (hasNext() == false) {
throw new NoSuchElementException(HashedMap.NO_NEXT_ENTRY);
throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
}
iCanRemove = true;
iIndex++;
@ -770,7 +770,7 @@ public class Flat3Map implements IterableMap {
public void remove() {
if (iCanRemove == false) {
throw new IllegalStateException(HashedMap.REMOVE_INVALID);
throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
}
iFlatMap.remove(getKey());
iIndex--;
@ -779,7 +779,7 @@ public class Flat3Map implements IterableMap {
public Object getKey() {
if (iCanRemove == false) {
throw new IllegalStateException(HashedMap.GETKEY_INVALID);
throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
}
switch (iIndex) {
case 3:
@ -794,7 +794,7 @@ public class Flat3Map implements IterableMap {
public Object getValue() {
if (iCanRemove == false) {
throw new IllegalStateException(HashedMap.GETVALUE_INVALID);
throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
}
switch (iIndex) {
case 3:
@ -809,7 +809,7 @@ public class Flat3Map implements IterableMap {
public Object setValue(Object value) {
if (iCanRemove == false) {
throw new IllegalStateException(HashedMap.SETVALUE_INVALID);
throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
}
Object old = getValue();
switch (iIndex) {

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/IdentityMap.java,v 1.1 2003/12/02 21:57:08 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/IdentityMap.java,v 1.2 2003/12/07 23:59:13 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -57,6 +57,10 @@
*/
package org.apache.commons.collections.map;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Map;
/**
@ -67,20 +71,21 @@ import java.util.Map;
* As a general rule, don't compare this map to other maps.
*
* @since Commons Collections 3.0
* @version $Revision: 1.1 $ $Date: 2003/12/02 21:57:08 $
* @version $Revision: 1.2 $ $Date: 2003/12/07 23:59:13 $
*
* @author java util HashMap
* @author Stephen Colebourne
*/
public class IdentityMap extends HashedMap {
public class IdentityMap extends AbstractHashedMap implements Serializable, Cloneable {
/** Serialisation version */
private static final long serialVersionUID = 2028493495224302329L;
/**
* Constructs a new empty map with default size and load factor.
*/
public IdentityMap() {
super();
super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
}
/**
@ -100,7 +105,7 @@ public class IdentityMap extends HashedMap {
* @param initialCapacity the initial capacity
* @param loadFactor the load factor
* @throws IllegalArgumentException if the initial capacity is less than one
* @throws IllegalArgumentException if the load factor is less than one
* @throws IllegalArgumentException if the load factor is less than zero
*/
public IdentityMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
@ -195,4 +200,30 @@ public class IdentityMap extends HashedMap {
}
}
//-----------------------------------------------------------------------
/**
* Clones the map without cloning the keys or values.
*
* @return a shallow clone
*/
public Object clone() {
return super.clone();
}
/**
* Write the map out using a custom routine.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
doWriteObject(out);
}
/**
* Read the map in using a custom routine.
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
doReadObject(in);
}
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/LRUMap.java,v 1.1 2003/12/07 01:23:54 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/LRUMap.java,v 1.2 2003/12/07 23:59:13 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -60,10 +60,9 @@ package org.apache.commons.collections.map;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Map;
import org.apache.commons.collections.OrderedMap;
/**
* A <code>Map</code> implementation with a fixed maximum size which removes
* the least recently used entry if an entry is added when full.
@ -86,16 +85,16 @@ import org.apache.commons.collections.OrderedMap;
* to least recently used.
*
* @since Commons Collections 3.0
* @version $Revision: 1.1 $ $Date: 2003/12/07 01:23:54 $
* @version $Revision: 1.2 $ $Date: 2003/12/07 23:59:13 $
*
* @author James Strachan
* @author Morgan Delagrange
* @author Stephen Colebourne
*/
public class LRUMap extends LinkedMap implements OrderedMap {
public class LRUMap extends AbstractLinkedMap implements Serializable, Cloneable {
/** Serialisation version */
static final long serialVersionUID = -2848625157350244215L;
static final long serialVersionUID = -612114643488955218L;
/** Default maximum size */
protected static final int DEFAULT_MAX_SIZE = 100;
@ -307,8 +306,32 @@ public class LRUMap extends LinkedMap implements OrderedMap {
//-----------------------------------------------------------------------
/**
* Write the data of subclasses.
* A sub-subclass must call super.doWriteObject().
* Clones the map without cloning the keys or values.
*
* @return a shallow clone
*/
public Object clone() {
return super.clone();
}
/**
* Write the map out using a custom routine.
*/
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
doWriteObject(out);
}
/**
* Read the map in using a custom routine.
*/
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
doReadObject(in);
}
/**
* Writes the data necessary for <code>put()</code> to work in deserialization.
*/
protected void doWriteObject(ObjectOutputStream out) throws IOException {
out.writeInt(maxSize);
@ -316,8 +339,7 @@ public class LRUMap extends LinkedMap implements OrderedMap {
}
/**
* Read the data of subclasses.
* A sub-subclass must call super.doReadObject().
* Reads the data necessary for <code>put()</code> to work in the superclass.
*/
protected void doReadObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
maxSize = in.readInt();

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/LinkedMap.java,v 1.3 2003/12/07 01:23:54 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/LinkedMap.java,v 1.4 2003/12/07 23:59:13 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -57,22 +57,15 @@
*/
package org.apache.commons.collections.map;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Map;
import java.util.NoSuchElementException;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.collections.MapIterator;
import org.apache.commons.collections.OrderedIterator;
import org.apache.commons.collections.OrderedMap;
import org.apache.commons.collections.OrderedMapIterator;
import org.apache.commons.collections.ResettableIterator;
/**
* A <code>Map</code> implementation that maintains the order of the entries.
* In this implementation order is maintained is by original insertion, but
* subclasses may work differently.
* In this implementation order is maintained is by original insertion.
* <p>
* This implementation improves on the JDK1.4 LinkedHashMap by adding the
* {@link org.apache.commons.collections.iterators.MapIterator MapIterator}
@ -90,24 +83,20 @@ import org.apache.commons.collections.ResettableIterator;
* methods exposed.
*
* @since Commons Collections 3.0
* @version $Revision: 1.3 $ $Date: 2003/12/07 01:23:54 $
* @version $Revision: 1.4 $ $Date: 2003/12/07 23:59:13 $
*
* @author java util LinkedHashMap
* @author Stephen Colebourne
*/
public class LinkedMap extends HashedMap implements OrderedMap {
public class LinkedMap extends AbstractLinkedMap implements Serializable, Cloneable {
/** Serialisation version */
static final long serialVersionUID = -1954063410665686469L;
private static final long serialVersionUID = 9077234323521161066L;
/** Header in the linked list */
protected transient LinkEntry header;
/**
* Constructs a new empty map with default size and load factor.
*/
public LinkedMap() {
super();
super(DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_THRESHOLD);
}
/**
@ -143,417 +132,30 @@ public class LinkedMap extends HashedMap implements OrderedMap {
super(map);
}
/**
* Initialise this subclass during construction.
*/
protected void init() {
header = new LinkEntry(null, -1, null, null);
header.before = header.after = header;
}
//-----------------------------------------------------------------------
/**
* Checks whether the map contains the specified value.
*
* @param value the value to search for
* @return true if the map contains the value
* Clones the map without cloning the keys or values.
*
* @return a shallow clone
*/
public boolean containsValue(Object value) {
// override uses faster iterator
if (value == null) {
for (LinkEntry entry = header.after; entry != header; entry = entry.after) {
if (entry.getValue() == null) {
return true;
}
}
} else {
for (LinkEntry entry = header.after; entry != header; entry = entry.after) {
if (isEqualValue(value, entry.getValue())) {
return true;
}
}
}
return false;
}
/**
* Clears the map, resetting the size to zero and nullifying references
* to avoid garbage collection issues.
*/
public void clear() {
// override to reset the linked list
super.clear();
header.before = header.after = header;
}
//-----------------------------------------------------------------------
/**
* Gets the first key in the map, which is the most recently inserted.
*
* @return the most recently inserted key
*/
public Object firstKey() {
if (size == 0) {
throw new NoSuchElementException("Map is empty");
}
return header.after.getKey();
}
/**
* Gets the last key in the map, which is the first inserted.
*
* @return the eldest key
*/
public Object lastKey() {
if (size == 0) {
throw new NoSuchElementException("Map is empty");
}
return header.before.getKey();
}
/**
* Gets the next key in sequence.
*
* @param key the key to get after
* @return the next key
*/
public Object nextKey(Object key) {
LinkEntry entry = (LinkEntry) getEntry(key);
return (entry == null || entry.after == header ? null : entry.after.getKey());
}
/**
* Gets the previous key in sequence.
*
* @param key the key to get before
* @return the previous key
*/
public Object previousKey(Object key) {
LinkEntry entry = (LinkEntry) getEntry(key);
return (entry == null || entry.before == header ? null : entry.before.getKey());
}
//-----------------------------------------------------------------------
/**
* Adds an entry into this map, maintaining insertion order.
* <p>
* This implementation adds the entry to the data storage table and
* to the end of the linked list.
*
* @param entry the entry to add
* @param hashIndex the index into the data array to store at
*/
protected void addEntry(HashEntry entry, int hashIndex) {
LinkEntry link = (LinkEntry) entry;
link.after = header;
link.before = header.before;
header.before.after = link;
header.before = link;
data[hashIndex] = entry;
public Object clone() {
return super.clone();
}
/**
* Creates an entry to store the data.
* <p>
* This implementation creates a new LinkEntry instance.
*
* @param next the next entry in sequence
* @param hashCode the hash code to use
* @param key the key to store
* @param value the value to store
* @return the newly created entry
* Write the map out using a custom routine.
*/
protected HashEntry createEntry(HashEntry next, int hashCode, Object key, Object value) {
return new LinkEntry(next, hashCode, key, value);
}
/**
* Removes an entry from the map and the linked list.
* <p>
* This implementation removes the entry from the linked list chain, then
* calls the superclass implementation.
*
* @param entry the entry to remove
* @param hashIndex the index into the data structure
* @param previous the previous entry in the chain
*/
protected void removeEntry(HashEntry entry, int hashIndex, HashEntry previous) {
LinkEntry link = (LinkEntry) entry;
link.before.after = link.after;
link.after.before = link.before;
link.after = null;
link.before = null;
super.removeEntry(entry, hashIndex, previous);
}
//-----------------------------------------------------------------------
/**
* Gets an iterator over the map.
* Changes made to the iterator affect this map.
* <p>
* A MapIterator returns the keys in the map. It also provides convenient
* methods to get the key and value, and set the value.
* It avoids the need to create an entrySet/keySet/values object.
* It also avoids creating the Mep Entry object.
*
* @return the map iterator
*/
public MapIterator mapIterator() {
if (size == 0) {
return IteratorUtils.EMPTY_ORDERED_MAP_ITERATOR;
}
return new LinkMapIterator(this);
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();
doWriteObject(out);
}
/**
* Gets a bidirectional iterator over the map.
* Changes made to the iterator affect this map.
* <p>
* A MapIterator returns the keys in the map. It also provides convenient
* methods to get the key and value, and set the value.
* It avoids the need to create an entrySet/keySet/values object.
* It also avoids creating the Mep Entry object.
*
* @return the map iterator
* Read the map in using a custom routine.
*/
public OrderedMapIterator orderedMapIterator() {
if (size == 0) {
return IteratorUtils.EMPTY_ORDERED_MAP_ITERATOR;
}
return new LinkMapIterator(this);
}
/**
* MapIterator
*/
static class LinkMapIterator extends LinkIterator implements OrderedMapIterator {
LinkMapIterator(LinkedMap map) {
super(map);
}
public Object next() {
return super.nextEntry().getKey();
}
public Object previous() {
return super.previousEntry().getKey();
}
public Object getKey() {
HashEntry current = currentEntry();
if (current == null) {
throw new IllegalStateException(HashedMap.GETKEY_INVALID);
}
return current.getKey();
}
public Object getValue() {
HashEntry current = currentEntry();
if (current == null) {
throw new IllegalStateException(HashedMap.GETVALUE_INVALID);
}
return current.getValue();
}
public Object setValue(Object value) {
HashEntry current = currentEntry();
if (current == null) {
throw new IllegalStateException(HashedMap.SETVALUE_INVALID);
}
return current.setValue(value);
}
}
//-----------------------------------------------------------------------
/**
* Creates an entry set iterator.
* Subclasses can override this to return iterators with different properties.
*
* @return the entrySet iterator
*/
protected Iterator createEntrySetIterator() {
if (size() == 0) {
return IteratorUtils.EMPTY_ORDERED_ITERATOR;
}
return new EntrySetIterator(this);
}
/**
* EntrySetIterator and MapEntry
*/
static class EntrySetIterator extends LinkIterator {
EntrySetIterator(LinkedMap map) {
super(map);
}
public Object next() {
return super.nextEntry();
}
public Object previous() {
return super.previousEntry();
}
}
//-----------------------------------------------------------------------
/**
* Creates a key set iterator.
* Subclasses can override this to return iterators with different properties.
*
* @return the keySet iterator
*/
protected Iterator createKeySetIterator() {
if (size() == 0) {
return IteratorUtils.EMPTY_ORDERED_ITERATOR;
}
return new KeySetIterator(this);
}
/**
* KeySetIterator
*/
static class KeySetIterator extends EntrySetIterator {
KeySetIterator(LinkedMap map) {
super(map);
}
public Object next() {
return super.nextEntry().getKey();
}
public Object previous() {
return super.previousEntry().getKey();
}
}
//-----------------------------------------------------------------------
/**
* Creates a values iterator.
* Subclasses can override this to return iterators with different properties.
*
* @return the values iterator
*/
protected Iterator createValuesIterator() {
if (size() == 0) {
return IteratorUtils.EMPTY_ORDERED_ITERATOR;
}
return new ValuesIterator(this);
}
/**
* ValuesIterator
*/
static class ValuesIterator extends LinkIterator {
ValuesIterator(LinkedMap map) {
super(map);
}
public Object next() {
return super.nextEntry().getValue();
}
public Object previous() {
return super.previousEntry().getValue();
}
}
//-----------------------------------------------------------------------
/**
* LinkEntry
*/
protected static class LinkEntry extends HashEntry {
protected LinkEntry before;
protected LinkEntry after;
protected LinkEntry(HashEntry next, int hashCode, Object key, Object value) {
super(next, hashCode, key, value);
}
}
/**
* Base Iterator
*/
protected static abstract class LinkIterator
implements OrderedIterator, ResettableIterator {
protected final LinkedMap map;
protected LinkEntry current;
protected LinkEntry next;
protected int expectedModCount;
protected LinkIterator(LinkedMap map) {
super();
this.map = map;
this.next = map.header.after;
this.expectedModCount = map.modCount;
}
public boolean hasNext() {
return (next != map.header);
}
public boolean hasPrevious() {
return (next.before != map.header);
}
protected LinkEntry nextEntry() {
if (map.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (next == map.header) {
throw new NoSuchElementException(HashedMap.NO_NEXT_ENTRY);
}
current = next;
next = next.after;
return current;
}
protected LinkEntry previousEntry() {
if (map.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
LinkEntry previous = next.before;
if (previous == map.header) {
throw new NoSuchElementException(HashedMap.NO_PREVIOUS_ENTRY);
}
next = previous;
current = previous;
return current;
}
protected LinkEntry currentEntry() {
return current;
}
public void remove() {
if (current == null) {
throw new IllegalStateException(HashedMap.REMOVE_INVALID);
}
if (map.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
map.remove(current.getKey());
current = null;
expectedModCount = map.modCount;
}
public void reset() {
current = null;
next = map.header.after;
}
public String toString() {
if (current != null) {
return "Iterator[" + current.getKey() + "=" + current.getValue() + "]";
} else {
return "Iterator[]";
}
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
doReadObject(in);
}
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/ListOrderedMap.java,v 1.7 2003/12/06 14:02:11 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/map/ListOrderedMap.java,v 1.8 2003/12/07 23:59:13 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -87,7 +87,7 @@ import org.apache.commons.collections.keyvalue.AbstractMapEntry;
* original position in the iteration.
*
* @since Commons Collections 3.0
* @version $Revision: 1.7 $ $Date: 2003/12/06 14:02:11 $
* @version $Revision: 1.8 $ $Date: 2003/12/07 23:59:13 $
*
* @author Henri Yandell
* @author Stephen Colebourne
@ -466,7 +466,7 @@ public class ListOrderedMap extends AbstractMapDecorator implements OrderedMap {
public void remove() {
if (readable == false) {
throw new IllegalStateException(HashedMap.REMOVE_INVALID);
throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
}
iterator.remove();
parent.map.remove(last);
@ -475,21 +475,21 @@ public class ListOrderedMap extends AbstractMapDecorator implements OrderedMap {
public Object getKey() {
if (readable == false) {
throw new IllegalStateException(HashedMap.GETKEY_INVALID);
throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
}
return last;
}
public Object getValue() {
if (readable == false) {
throw new IllegalStateException(HashedMap.GETVALUE_INVALID);
throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
}
return parent.get(last);
}
public Object setValue(Object value) {
if (readable == false) {
throw new IllegalStateException(HashedMap.SETVALUE_INVALID);
throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
}
return parent.map.put(last, value);
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestHashedMap.java,v 1.3 2003/12/07 01:22:50 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestHashedMap.java,v 1.4 2003/12/07 23:59:12 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -67,7 +67,7 @@ import org.apache.commons.collections.BulkTest;
/**
* JUnit tests.
*
* @version $Revision: 1.3 $ $Date: 2003/12/07 01:22:50 $
* @version $Revision: 1.4 $ $Date: 2003/12/07 23:59:12 $
*
* @author Stephen Colebourne
*/
@ -95,8 +95,8 @@ public class TestHashedMap extends AbstractTestIterableMap {
// public void testCreate() throws Exception {
// resetEmpty();
// writeExternalFormToDisk((Serializable) map, "D:/dev/collections/data/test/HashedMap.emptyCollection.version3.obj");
// writeExternalFormToDisk((java.io.Serializable) map, "D:/dev/collections/data/test/HashedMap.emptyCollection.version3.obj");
// resetFull();
// writeExternalFormToDisk((Serializable) map, "D:/dev/collections/data/test/HashedMap.fullCollection.version3.obj");
// writeExternalFormToDisk((java.io.Serializable) map, "D:/dev/collections/data/test/HashedMap.fullCollection.version3.obj");
// }
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestIdentityMap.java,v 1.3 2003/12/07 01:22:50 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestIdentityMap.java,v 1.4 2003/12/07 23:59:12 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -57,6 +57,8 @@
*/
package org.apache.commons.collections.map;
import java.io.IOException;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
@ -70,7 +72,7 @@ import org.apache.commons.collections.IterableMap;
/**
* JUnit tests.
*
* @version $Revision: 1.3 $ $Date: 2003/12/07 01:22:50 $
* @version $Revision: 1.4 $ $Date: 2003/12/07 23:59:12 $
*
* @author Stephen Colebourne
*/
@ -152,13 +154,26 @@ public class TestIdentityMap extends AbstractTestObject {
assertEquals(false, entry1.equals(entry3));
}
/**
* Compare the current serialized form of the Map
* against the canonical version in CVS.
*/
public void testEmptyMapCompatibility() throws IOException, ClassNotFoundException {
// test to make sure the canonical form has been preserved
Map map = (Map) makeObject();
if (map instanceof Serializable && !skipSerializedCanonicalTests()) {
Map map2 = (Map) readExternalFormFromDisk(getCanonicalEmptyCollectionName(map));
assertEquals("Map is empty", 0, map2.size());
}
}
// public void testCreate() throws Exception {
// Map map = new IdentityMap();
// writeExternalFormToDisk((Serializable) map, "D:/dev/collections/data/test/IdentityMap.emptyCollection.version3.obj");
// writeExternalFormToDisk((java.io.Serializable) map, "D:/dev/collections/data/test/IdentityMap.emptyCollection.version3.obj");
// map = new IdentityMap();
// map.put(I1A, I2A);
// map.put(I1B, I2A);
// map.put(I2A, I2B);
// writeExternalFormToDisk((Serializable) map, "D:/dev/collections/data/test/IdentityMap.fullCollection.version3.obj");
// writeExternalFormToDisk((java.io.Serializable) map, "D:/dev/collections/data/test/IdentityMap.fullCollection.version3.obj");
// }
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestLRUMap.java,v 1.1 2003/12/07 01:23:54 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestLRUMap.java,v 1.2 2003/12/07 23:59:12 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -74,7 +74,7 @@ import org.apache.commons.collections.ResettableIterator;
/**
* JUnit tests.
*
* @version $Revision: 1.1 $ $Date: 2003/12/07 01:23:54 $
* @version $Revision: 1.2 $ $Date: 2003/12/07 23:59:12 $
*
* @author Stephen Colebourne
*/
@ -339,8 +339,8 @@ public class TestLRUMap extends AbstractTestOrderedMap {
// public void testCreate() throws Exception {
// resetEmpty();
// writeExternalFormToDisk((Serializable) map, "D:/dev/collections/data/test/LRUMap.emptyCollection.version3.obj");
// writeExternalFormToDisk((java.io.Serializable) map, "D:/dev/collections/data/test/LRUMap.emptyCollection.version3.obj");
// resetFull();
// writeExternalFormToDisk((Serializable) map, "D:/dev/collections/data/test/LRUMap.fullCollection.version3.obj");
// writeExternalFormToDisk((java.io.Serializable) map, "D:/dev/collections/data/test/LRUMap.fullCollection.version3.obj");
// }
}

View File

@ -1,5 +1,5 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestLinkedMap.java,v 1.2 2003/12/07 01:22:50 scolebourne Exp $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/map/TestLinkedMap.java,v 1.3 2003/12/07 23:59:12 scolebourne Exp $
* ====================================================================
*
* The Apache Software License, Version 1.1
@ -72,7 +72,7 @@ import org.apache.commons.collections.ResettableIterator;
/**
* JUnit tests.
*
* @version $Revision: 1.2 $ $Date: 2003/12/07 01:22:50 $
* @version $Revision: 1.3 $ $Date: 2003/12/07 23:59:12 $
*
* @author Stephen Colebourne
*/
@ -161,8 +161,8 @@ public class TestLinkedMap extends AbstractTestOrderedMap {
// public void testCreate() throws Exception {
// resetEmpty();
// writeExternalFormToDisk((Serializable) map, "D:/dev/collections/data/test/LinkedMap.emptyCollection.version3.obj");
// writeExternalFormToDisk((java.io.Serializable) map, "D:/dev/collections/data/test/LinkedMap.emptyCollection.version3.obj");
// resetFull();
// writeExternalFormToDisk((Serializable) map, "D:/dev/collections/data/test/LinkedMap.fullCollection.version3.obj");
// writeExternalFormToDisk((java.io.Serializable) map, "D:/dev/collections/data/test/LinkedMap.fullCollection.version3.obj");
// }
}