Mega Code Archive

 
Categories / Java / Collections Data Structure
 

List Map

/*  *   * JAFFA - Java Application Framework For All  *  * Copyright (C) 2002 JAFFA Development Group  *  *     This library is free software; you can redistribute it and/or  *     modify it under the terms of the GNU Lesser General Public  *     License as published by the Free Software Foundation; either  *     version 2.1 of the License, or (at your option) any later version.  *  *     This library is distributed in the hope that it will be useful,  *     but WITHOUT ANY WARRANTY; without even the implied warranty of  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  *     Lesser General Public License for more details.  *  *     You should have received a copy of the GNU Lesser General Public  *     License along with this library; if not, write to the Free Software  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  *  * Redistribution and use of this software and associated documentation ("Software"),  * with or without modification, are permitted provided that the following conditions are met:  * 1. Redistributions of source code must retain copyright statements and notices.  *         Redistributions must also contain a copy of this document.  * 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 name "JAFFA" must not be used to endorse or promote products derived from  *  this Software without prior written permission. For written permission,  *  please contact mail to: jaffagroup@yahoo.com.  * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"  *  appear in their names without prior written permission.  * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).  *  * 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.  *   */ import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /**  * This class combines the utility of the HashMap & the ListSet. Features are:  * 1) Ensure quick random access to an object using a 'key'  * 2) Iterate through the Map in the order in which the entries were made  */ public class ListMap implements Map, Cloneable, Serializable {     private static final int TYPE_KEY_SET = 0;     private static final int TYPE_ENTRY_SET = 1;     private static final int TYPE_VALUES = 2;     /** This Map will contain the key-value pairs */     private HashMap m_map = null;     /** This List will maintain the keys in the order of entry */     private ListSet m_list = null;     // fields to hold the return Collections.     private transient Set m_keySet = null;     private transient Set m_entrySet = null;     private transient Collection m_values = null;     /** Creates new ListMap */     public ListMap() {         m_map = new HashMap();         m_list = new ListSet();     }     /** Creates new ListMap specifying the initial capacity.      * @param initialCapacity The initial capacity.      */     public ListMap(int initialCapacity) {         m_map = new HashMap(initialCapacity);         m_list = new ListSet(initialCapacity);     }     /** Creates new ListMap specifying the initial capacity and load factor.      * @param initialCapacity The initial capacity.      * @param loadFactor The loadFactor.      */     public ListMap(int initialCapacity, float loadFactor) {         m_map = new HashMap(initialCapacity, loadFactor);         m_list = new ListSet(initialCapacity);     }     /** Creates new ListMap from an existing Map      * @param t An existing Map.      */     public ListMap(Map t) {         if (t == null) {             m_map = new HashMap();             m_list = new ListSet();         } else {             m_map = new HashMap(t);             m_list = new ListSet( t.keySet() );         }     }     // *** MAP INTERFACE METHODS ***     /** Adds an object to the Map. If the map previously contained a mapping for this key, the old value is replaced by the specified value.      * @param key The key used for adding the object.      * @param value The object to be added.      * @return previous value associated with specified key, or null if there was no mapping for key. A null return can also indicate that the map previously associated null with the specified key.      */     public Object put(Object key, Object value) {         return put(-1, key, value);     }     /** Removes the mapping for this key from this map if it is present.      * @param key key whose mapping is to be removed from the map.      * @return previous value associated with specified key, or null if there was no mapping for key.      */     public Object remove(Object key) {         m_list.remove(key);         return m_map.remove(key);     }     /** Returns a set view of the keys contained in this map.      * @return a set view of the keys contained in this map.      */     public Set keySet() {         if (m_keySet == null)             m_keySet = new ListMap.KeySet();         return m_keySet;     }     /** Removes all mappings from this map .*/     public void clear() {         m_list.clear();         m_map.clear();     }     /** Returns a collection view of the values contained in this map.      * @return a collection view of the values contained in this map.      */     public Collection values() {         if (m_values == null)             m_values = new ListMap.Values();         return m_values;     }     /** Returns the hash code value for this map.      * @return the hash code value for this map.      */     public int hashCode() {         return m_map.hashCode();     }     /** Returns true if this map contains a mapping for the specified key.      * @param key key whose presence in this map is to be tested.      * @return true if this map contains a mapping for the specified key.      */     public boolean containsKey(Object key) {         return m_map.containsKey(key);     }     /** Returns the number of key-value mappings in this map.      * @return the number of key-value mappings in this map.      */     public int size() {         return m_map.size();     }     /** Returns a set view of the mappings contained in this map.      * @return a set view of the mappings contained in this map.      */     public Set entrySet() {         if (m_entrySet == null)             m_entrySet = new ListMap.EntrySet();         return m_entrySet;     }     /** Returns true if this map maps one or more keys to the specified value.      * @param value value whose presence in this map is to be tested.      * @return true if this map maps one or more keys to the specified value.      */     public boolean containsValue(Object value) {         return m_map.containsValue(value);     }     /** Copies all of the mappings from the specified map to this map.      * @param t Mappings to be stored in this map.      */     public void putAll(Map t) {         if (t != null) {             m_map.putAll(t);             m_list.addAll( t.keySet() );         }     }     /** Compares the specified object with this map for equality.      * Returns true if the given object is also a ListMap and the two Maps represent the same mappings.      * @param o object to be compared for equality with this map.      * @return true if the specified object is equal to this map.      */     public boolean equals(Object o) {         boolean result = false;         if (o instanceof ListMap) {             ListMap listMap = (ListMap) o;             result = m_map.equals(listMap.m_map);         }         return result;     }     /** Returns true if this map contains no key-value mappings.      * @return true if this map contains no key-value mappings.      */     public boolean isEmpty() {         return m_map.isEmpty();     }     /** Returns the value to which this map maps the specified key.      * Returns null if the map contains no mapping for this key.      * A return value of null does not necessarily indicate that the map contains no mapping for the key; it's also possible that the map explicitly maps the key to null.      * The containsKey operation may be used to distinguish these two cases.      * @param key key whose associated value is to be returned.      * @return the value to which this map maps the specified key, or null if the map contains no mapping for this key.      */     public Object get(Object key) {         return m_map.get(key);     }     // *** Additional Methods ***     /** Adds an object to the Map. If the map previously contained a mapping for this key, the old value is replaced by the specified value.      * @param index The position at which the the object will be added.      * @param key The key used for adding the object.      * @param value The object to be added.      * @return previous value associated with specified key, or null if there was no mapping for key. A null return can also indicate that the map previously associated null with the specified key.      */     public Object put(int index, Object key, Object value) {         Object returnObject = m_map.put(key, value);         // add to the list, only if it doesn't already exist         if ( !m_list.contains(key) ) {             if (index == -1)                 m_list.add(key);             else                 m_list.add(index, key);         }         return returnObject;     }     /** Returns the mapping at the specified index.      * @param index The position at from which the mapping is to be retrieved.      * @return the mapping at the specified index.      */     public Object getByIndex(int index) {         return get( m_list.get(index) );     }     /** Removes the mapping for this index from this map if it is present.      * @param index The position at from which the mapping is to be removed.      * @return previous value associated with position, or null if there was no mapping for position.      */     public Object remove(int index) {         return remove( m_list.get(index) );     }     /** Returns the index in this Map of the specified key.      * A '-1' is returned in case no such key exists.      * @param key The key used for adding the object.      * @return the index in this Map of the specified key.      */     public int indexOf(Object key) {         return m_list.indexOf(key);     }     // *** CLONEABLE INTERFACE METHODS ***     /** Returns a clone of the Map.      * @throws CloneNotSupportedException if cloning is not supported. Should never happen.      * @return a clone of the Map.      */     public Object clone() throws CloneNotSupportedException {         ListMap obj = (ListMap) super.clone();         if (m_map != null && m_map instanceof HashMap)             obj.m_map = (HashMap) m_map.clone();         if (m_list != null && m_list instanceof ListSet)             obj.m_list = (ListSet) m_list.clone();         // reset the transient fields         obj.m_keySet = null;         obj.m_entrySet = null;         obj.m_values = null;         return obj;     }     // *** PRIVATE METHODS ***     private Iterator getIterator(int type) {         return new ListMap.ListMapIterator(type);     }     private Map.Entry getEntry(Object key, Object value) {         return new ListMap.ListMapEntry(key, value);     }     // *** INNER CLASSES ***     private class ListMapIterator implements Iterator {         private Iterator m_iterator = null;         private int m_type;         private Object m_lastReturned = null;         private ListMapIterator(int type) {             m_type = type;             m_iterator = ListMap.this.m_list.iterator();         }         /** Returns true if the iteration has more elements.          * @return true if the iteration has more elements.          */         public boolean hasNext() {             return m_iterator.hasNext();         }         /** Returns the next element in the iteration.          * @return the next element in the iteration.          */         public Object next() {             m_lastReturned = m_iterator.next();             Object obj = null;             switch (m_type) {                 case TYPE_KEY_SET :                     obj = m_lastReturned;                     break;                 case TYPE_ENTRY_SET :                     Object value = ListMap.this.get(m_lastReturned);                     obj = ListMap.this.getEntry(m_lastReturned, value);                     break;                 case TYPE_VALUES :                     obj = ListMap.this.get(m_lastReturned);                     break;             }             return obj;         }         /** Removes from the underlying collection the last element returned by the iterator.          */         public void remove() {             m_iterator.remove();             ListMap.this.remove(m_lastReturned);         }     }     private class KeySet extends AbstractSet {         /** Returns the number of elements in this set.          * @return the number of elements in this set.          */         public int size() {             return ListMap.this.size();         }         /** Returns true if this set contains the specified element.          * @param o element whose presence in this set is to be tested.          * @return true if this set contains the specified element.          */         public boolean contains(Object o) {             return ListMap.this.containsKey(o);         }         /** Removes the specified element from this set if it is present.          * @param o object to be removed from this set, if present.          * @return true if the set contained the specified element.          */         public boolean remove(Object o) {             int i = size();             ListMap.this.remove(o);             return ( size() != i );         }         /** Removes all of the elements from this set.*/         public void clear() {             ListMap.this.clear();         }         /** Returns an iterator over the elements in this set.          * @return an iterator over the elements in this set.          */         public Iterator iterator() {             return ListMap.this.getIterator(ListMap.TYPE_KEY_SET);         }     }     private class EntrySet extends AbstractSet {         /** Returns the number of elements in this set.          * @return the number of elements in this set.          */         public int size() {             return ListMap.this.size();         }         /** Returns true if this set contains the specified element.          * @param o element whose presence in this set is to be tested.          * @return true if this set contains the specified element.          */         public boolean contains(Object o) {             boolean result = false;             if (o != null && o instanceof Map.Entry) {                 Map.Entry entry = (Map.Entry) o;                 Object key = entry.getKey();                 if ( ListMap.this.containsKey(key) ) {                     Object value1 = ListMap.this.get(key);                     Object value2 = entry.getValue();                     result = ( value1 == null ? value2 == null : value1.equals(value2) );                 }             }             return result;         }         /** Removes the specified element from this set if it is present.          * @param o object to be removed from this set, if present.          * @return true if the set contained the specified element.          */         public boolean remove(Object o) {             boolean result = false;             if (o != null && o instanceof Map.Entry) {                 Map.Entry entry = (Map.Entry) o;                 Object key = entry.getKey();                 int i = size();                 ListMap.this.remove(key);                 result = ( i != size() );             }             return result;         }         /** Removes all of the elements from this set.*/         public void clear() {             ListMap.this.clear();         }         /** Returns an iterator over the elements in this set.          * @return an iterator over the elements in this set.          */         public Iterator iterator() {             return ListMap.this.getIterator(ListMap.TYPE_ENTRY_SET);         }     }     private class Values extends AbstractCollection {         /** Returns the number of elements in this collection.          * @return the number of elements in this collection.          */         public int size() {             return ListMap.this.size();         }         /** Returns true if this collection contains the specified element.          * @param o element whose presence in this collection is to be tested.          * @return true if this collection contains the specified element.          */         public boolean contains(Object o) {             return ListMap.this.containsValue(o);         }         /** Removes all of the elements from this collection.          */         public void clear() {             ListMap.this.clear();         }         /** Returns an iterator over the elements in this collection.          * @return an iterator over the elements in this collection.          */         public Iterator iterator() {             return ListMap.this.getIterator(ListMap.TYPE_VALUES);         }     }     private class ListMapEntry implements Map.Entry {         Object m_key = null;         Object m_value = null;         private ListMapEntry(Object key, Object value) {             m_key = key;             m_value = value;         }         /** Returns the key corresponding to this entry.          * @return the key corresponding to this entry.          */         public Object getKey() {             return m_key;         }         /** Returns the hash code value for this map entry.          * @return the hash code value for this map entry.          */         public int hashCode() {             return (m_key==null ? 0 : m_key.hashCode() )             + (m_value==null ? 0 : m_value.hashCode() );         }         /** Returns the value corresponding to this entry.          * @return the value corresponding to this entry.          */         public Object getValue() {             return m_value;         }         /** This is an Unsupported method. It throws the UnsupportedOperationException.          * @param value the value to be set.          * @return old value corresponding to the entry.          */         public Object setValue(Object value) {             throw new UnsupportedOperationException();         }         /** Compares the specified object with this entry for equality.          * Returns true if the given object is also a ListMap.Entry object and the two entries represent the same mapping.          * @param o object to be compared for equality with this map entry.          * @return true if the specified object is equal to this map entry.          */         public boolean equals(Object o) {             boolean result = false;             if (o instanceof ListMap.ListMapEntry) {                 ListMap.ListMapEntry e2 = (ListMap.ListMapEntry) o;                 result =  ( getKey()==null ?                 e2.getKey()==null : getKey().equals( e2.getKey() ) )                 && ( getValue()==null ?                 e2.getValue()==null : getValue().equals(e2.getValue() ) );             }             return result;         }     } } /*  *   * JAFFA - Java Application Framework For All  *  * Copyright (C) 2002 JAFFA Development Group  *  *     This library is free software; you can redistribute it and/or  *     modify it under the terms of the GNU Lesser General Public  *     License as published by the Free Software Foundation; either  *     version 2.1 of the License, or (at your option) any later version.  *  *     This library is distributed in the hope that it will be useful,  *     but WITHOUT ANY WARRANTY; without even the implied warranty of  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  *     Lesser General Public License for more details.  *  *     You should have received a copy of the GNU Lesser General Public  *     License along with this library; if not, write to the Free Software  *     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  *  * Redistribution and use of this software and associated documentation ("Software"),  * with or without modification, are permitted provided that the following conditions are met:  * 1. Redistributions of source code must retain copyright statements and notices.  *         Redistributions must also contain a copy of this document.  * 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 name "JAFFA" must not be used to endorse or promote products derived from  *  this Software without prior written permission. For written permission,  *  please contact mail to: jaffagroup@yahoo.com.  * 4. Products derived from this Software may not be called "JAFFA" nor may "JAFFA"  *  appear in their names without prior written permission.  * 5. Due credit should be given to the JAFFA Project (http://jaffa.sourceforge.net).  *  * 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 class is backed by an ArrayList. Features are  * 1) Ensure the Set functionality of unique elements(including a null) in this data structure  * 2) Iterate through the list in the order in which the entries were made  */ class ListSet implements Set, Cloneable, Serializable {     List m_set = null;     /** Creates new ListSet */     public ListSet() {         m_set = new ArrayList();     }     /** Creates new ListSet specifying the initial capacity.      * @param initialCapacity The initial capacity.      */     public ListSet(int initialCapacity) {         m_set = new ArrayList(initialCapacity);     }     /** Creates new ListSet from an existing Collection.      * @param c An existing collection.      */     public ListSet(Collection c) {         m_set = new ArrayList();         if (c != null) {             for (Iterator itr = c.iterator(); itr.hasNext();) {                 Object obj = itr.next();                 if ( !m_set.contains(obj) )                     m_set.add(obj);             }         }     }     // *** Set Interface methods ***     /** Retains only the elements in this set that are contained in the specified collection.      * @param c collection that defines which elements this set will retain.      * @return true if this collection changed as a result of the call.      */     public boolean retainAll(Collection c) {         return m_set.retainAll(c);     }     /** Returns true if this set contains the specified element.      * @param o element whose presence in this set is to be tested.      * @return true if this set contains the specified element.      */     public boolean contains(Object o) {         return m_set.contains(o);     }     /** Returns an array containing all of the elements in this set.      * @return an array containing all of the elements in this set.      */     public Object[] toArray() {         return m_set.toArray();     }     /** Returns an array containing all of the elements in this set; the runtime type of the returned array is that of the specified array.      * @param a the array into which the elements of this set are to be stored, if it is big enough; otherwise, a new array of the same runtime type is allocated for this purpose.      * @return an array containing all of the elements in this set; the runtime type of the returned array is that of the specified array      */     public Object[] toArray(Object[] a) {         return m_set.toArray(a);     }     /** Returns an iterator over the elements in this set.      * @return an iterator over the elements in this set.      */     public Iterator iterator() {         return m_set.iterator();     }     /** Removes from this set all of its elements that are contained in the specified collection.      * @param c collection that defines which elements will be removed from this set.      * @return true if this set changed as a result of the call.      */     public boolean removeAll(Collection c) {         return m_set.removeAll(c);     }     /** Removes the specified element from this set if it is present.      * @param o object to be removed from this set, if present.      * @return true if the set contained the specified element.      */     public boolean remove(Object o) {         return m_set.remove(o);     }     /** Removes all of the elements from this set.*/     public void clear() {         m_set.clear();     }     /** Returns the hash code value for this set.      * @return the hash code value for this set.      */     public int hashCode() {         return m_set.hashCode();     }     /** Adds all of the elements in the specified collection to this set if they're not already present.      * @param c collection whose elements are to be added to this set.      * @return true if this set changed as a result of the call.      */     public boolean addAll(Collection c) {         boolean added = false;         if (c != null) {             for (Iterator itr = c.iterator(); itr.hasNext();) {                 Object obj = itr.next();                 if ( !m_set.contains(obj) ) {                     m_set.add(obj);                     added = true;                 }             }         }         return added;     }     /** Returns the number of elements in this set.      * @return the number of elements in this set.      */     public int size() {         return m_set.size();     }     /** Returns true if this set contains all of the elements of the specified collection.      * @param c collection to be checked for containment in this set.      * @return true if this set contains all of the elements of the specified collection.      */     public boolean containsAll(Collection c) {         return m_set.containsAll(c);     }     /** Adds the specified element to this set if it is not already present.      * @param o element to be added to this set.      * @return true if this set did not already contain the specified element.      */     public boolean add(Object o) {         boolean added = false;         if ( !m_set.contains(o) ) {             m_set.add(o);             added = true;         }         return added;     }     /** Compares the specified object with this set for equality.      * @param o Object to be compared for equality with this set.      * @return true if the specified Object is equal to this set.      */     public boolean equals(Object o) {         return m_set.equals(o);     }     /** Returns true if this set contains no elements.      * @return true if this set contains no elements.      */     public boolean isEmpty() {         return m_set.isEmpty();     }     // *** Additional Methods ***     /** Adds the specified element to this set if it is not already present, at the specified index.      * @param index The position at which the element is to be added.      * @param o element to be added to this set.      */     public void add(int index, Object o) {         if ( !m_set.contains(o) )             m_set.add(index, o);     }     /** Returns the element from the specified position.      * @param index The position from which the element is to be retrieved.      * @return the element from the specified position.      */     public Object get(int index) {         return m_set.get(index);     }     /** Remove the element from the specified position.      * @param index The position from which the element is to be removed.      * @return the element being removed.      */     public Object remove(int index) {         return m_set.remove(index);     }     /** Returns the index of the element in this set.      * @param o The element whose index is to be found.      * @return the index of the element in this set.      */     public int indexOf(Object o) {         return m_set.indexOf(o);     }     // *** CLONEABLE INTERFACE METHODS ***     /** Returns a clone of the Set.      * @throws CloneNotSupportedException if cloning is not supported. Should never happen.      * @return a clone of the Set.      */     public Object clone() throws CloneNotSupportedException {         Object obj = super.clone();         if (m_set != null && m_set instanceof Cloneable)             ( (ListSet) obj ).m_set = (List) ( (ArrayList) m_set ).clone();         return obj;     } }