Mega Code Archive

 
Categories / Java / Collections Data Structure
 

BinaryMap class implements a map from objects to integer objects where the only value is the integer with value

/*  * LingPipe v. 3.9  * Copyright (C) 2003-2010 Alias-i  *  * This program is licensed under the Alias-i Royalty Free License  * Version 1 WITHOUT ANY WARRANTY, without even the implied warranty of  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the Alias-i  * Royalty Free License Version 1 for more details.  *  * You should have received a copy of the Alias-i Royalty Free License  * Version 1 along with this program; if not, visit  * http://alias-i.com/lingpipe/licenses/lingpipe-license-1.txt or contact  * Alias-i, Inc. at 181 North 11th Street, Suite 401, Brooklyn, NY 11211,  * +1 (718) 290-9170. ectends */ //package com.aliasi.util; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Serializable; import java.util.AbstractCollection; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /**  * The <code>BinaryMap</code> class implements a map from objects to  * integer objects where the only value is the integer with value 1.  * Instances of this class are typically returned by boolean feature  * extractors, who return a map with only 1 values, because the 0  * values are implicit.  *  * <p>Binary maps are based on a set of keys that map to 1.  Thus  * they are more space efficient than Java's utility maps such  * as tree maps or hash maps.  The underlying set implementation is  * pluggable, but must be mutable if the resulting binary map  * is to be mutable.  *  * <p>Modifiability through the entry set, key set, and values  * collection is fully supported through their respective iterators  * and through the collections themselves.  The map entries making  * up the entry set may not have their values modified.  *  * <h3>Equality and Hash Codes</h3>  *  * Binary maps satisfy the requirements laid out for hash codes  * and object equality specified in {@link Map}.  *   * <h3>Thread Safety</h3>  *  * Binary maps have the same thread safety as their underlying  * sets.    *  * <h3>Serialization</h3>  *  * A binary map may be serialized if its underlying positive  * set is serializable.  *  * @author  Bob Carpenter  * @version 3.9.1  * @since   LingPipe3.9.1  * @param <E> the type of keys in the map  */ public class BinaryMap<E>      extends AbstractMap<E,Integer>      implements Serializable {     static final long serialVersionUID = -6965494782866980478L;     private final Set<E> mPositiveSet;     /**      * Construct a binary map based on the specified set of positive      * values.  The set is stored as is rather than copied.  The      * set must be mutable if the constructed map is to be mutable.      *      * @param positiveSet      */     public BinaryMap(Set<E> positiveSet) {         mPositiveSet = positiveSet;     }     /**      * Adds the specified element to the map with value 1.      *      * <p>Note that this method is not part of the {@link Map}      * interface.      *      * @param e Element added to the map with value 1.      * @return {@code true} if the map didn't already contain the      * element.      * @throws UnsupportedOperationException If the underlying set of      * positive elements does not support {@code Collection.add}.      */     public boolean add(E e) {         return mPositiveSet.add(e);     }     /**      * Returns the set of keys mapping to 1.  This set is backed by      * this map, so changes to the set are reflected in the map and      * vice-versa.        *      * <p>If the underlying set is not modifiable, attempts to      * modify the key set will raise unsupported operation exceptions.      *      * <p>Note that results are undefined in the middle of an      * iterator, which will likely throw concurrent modification, null      * pointer, or array index out of bounds exceptions.  Therefore,      * access to the key set must be synchronized in the same way      * access to the underlying set.      *      * <p>Further note that unlike the specification in {@link Map},      * the returned keyset supports the {@link Collection#add(Object)} and      * {@link Collection#addAll(Collection)}.  Adding elements to the key set      * is the same as adding them through the {@link #add(Object)}      * method of this class.      *      * @return The set of keys mapping to one.      */     public Set<E> keySet() {         return mPositiveSet;     }     /**      * Return a set view of the mappings in this map.  This set is      * backed by this map, so changes to the return set affect      * this set and vice-versa.  The resulting set supports deletions      * through {@code Iterator.remove}, {@code Collection.remove},      * {@code Collection.removeAll}, {@code Collection.retainAll},      * and {@code Collection.clear} operations, but does not support      * the add operations.      *      * <p>If the underlying set of positive elements does not      * support these modification operations, they will throw an      * unsupported operation exception.      *      * <p><i>Implementation Note:</i> The {@code Map.Entry} elements      * are created as necessary by the entry set using a relatively      * efficient implementation of entries that only stores the key.      * Accessing the key set is more efficient.      *      * @return The set of mappings for this map.       */     @Override     public Set<Map.Entry<E,Integer>> entrySet() {         return new EntrySet();     }     /**      * Returns the {@code Integer} with value 1 if the specified      * argument is mapped to 1 by this map, and returns null      * otherwise.      *      * <p>The constant {@link #ONE} is used for the return      * value.      *      * @param key The element whose value is returned.      * @return 1 if the element is mapped to 1, and {@code null}      * otherwise.      */     @Override     public Integer get(Object key) {         return mPositiveSet.contains(key)             ? ONE             : null;     }     /**      * Remove the mapping with the specified key if it is present, returning      * the previous value, or {@code null} if it was previously undefined.      *      * @param key Key of mapping to remove.      * @return The value 1 if the key is already present, and {@code      * null} otherwise.      * @throws UnsupportedOperationException If the underlying set for      * this map does not support the {@link Set#remove(Object)} operation.      */     @Override     public Integer remove(Object key) {         return mPositiveSet.remove(key)             ? ONE             : null;     }     /**      * Returns the size of this mapping.      *      * @return Size of this mapping.      */     @Override     public int size() {         return mPositiveSet.size();     }     /**      * Returns an immutable collection view of the values for this      * map.  The resulting collecton will be empty if the map is      * entry, or contain the single value {@link #ONE} if the map is      * not empty.      */     @Override     public Collection<Integer> values() {         return new Values();     }     /**      * Removes all of the mappings from this map.      *      * <p>The implementation just delegates the clear operation to the      * contained set.      *      * @throws UnsupportedOperationException If the clear operation      * is not supported by the contained set.      */     @Override     public void clear() {         mPositiveSet.clear();     }     /**      * Returns {@code true} if this mapping contains a mapping      * for the specified object.      *      * <p>This method delegates to the underlying positive set's      * {@code Collection.contains} method.      *      * @param o Object to teset.      * @return {@code true} if it is mapped by this mapping.      * @throws ClassCastException If the underlying set throws      * a class cast when checking the specified object for      * membership.      */     @Override     public boolean containsKey(Object o) {         return mPositiveSet.contains(o);     }     /**      * Returns {@code true} if this map contains a mapping      * from some key to the specified value.        *      * <p>Note that the only object for which this map may      * return true is the {@code Integer} with value 1.      *      * @param o Object to test.      * @return {@code true} if this map contains a mapping      * from some object to this value.      */     @Override     public boolean containsValue(Object o) {         return ONE.equals(o) && !isEmpty();     }     /**      * Returns {@code true} if this mapping is empty.      *      * @return {@code true} if this mapping is empty.      */     @Override     public boolean isEmpty() {         return mPositiveSet.isEmpty();     }     /**      * Adds the mapping of the specified object to the specified      * value if the specified number is the {@code Integer} with      * value 1.      *      * @param e Key for the mapping.      * @param n Value for the mapping.      * @throws IllegalArgumentException If the specified integer      * does not have value 1.      */     @Override     public Integer put(E e, Integer n) {         if (!ONE.equals(n))             throw new IllegalArgumentException();         return  mPositiveSet.add(e) ? null : ONE;     }     class Values extends AbstractCollection<Integer> {         @Override         public Iterator<Integer> iterator() {             return new ValuesIterator();         }         @Override         public int size() {             return isEmpty() ? 0 : 1;         }         @Override         public void clear() {             mPositiveSet.clear();         }         @Override         public boolean contains(Object o) {             return ONE.equals(o) && !isEmpty();         }         @Override         public boolean isEmpty() {             return mPositiveSet.isEmpty();         }         @Override         public boolean remove(Object o) {             if (!ONE.equals(o))                 return false;             boolean removedSomething = !isEmpty();             mPositiveSet.clear();             return removedSomething;         }         @Override         public boolean removeAll(Collection<?> c) {             if (!c.contains(ONE))                 return false;             boolean removedSomething = !isEmpty();             mPositiveSet.clear();             return removedSomething;         }         @Override         public boolean retainAll(Collection<?> c) {             if (isEmpty()) return false;             if (c.contains(ONE)) return false;             mPositiveSet.clear();             return true;         }         @Override         public Object[] toArray() {             return isEmpty()                  ? EMPTY_OBJECT_ARRAY                  : new Object[] { ONE };         }     }     class ValuesIterator implements Iterator<Integer> {         boolean finished = mPositiveSet.isEmpty();         boolean mayRemove = false;         public boolean hasNext() {             return !finished;         }         public Integer next() {             if (!hasNext())                 throw new NoSuchElementException();             finished = true;             mayRemove = true;             return ONE;         }         public void remove() {             if (!mayRemove)                 throw new IllegalStateException();             mayRemove = false;             mPositiveSet.clear();         }     }     class EntrySet extends AbstractSet<Map.Entry<E,Integer>> {         @Override         public int size() {             return mPositiveSet.size();         }         @Override         public Iterator<Map.Entry<E,Integer>> iterator() {             return new EntrySetIterator();         }         @Override         public void clear() {             mPositiveSet.clear();         }         @Override         public boolean contains(Object o) {             if (!(o instanceof Map.Entry<?,?>))                 return false;             @SuppressWarnings("unchecked") // checked above             Map.Entry<?,?> entry = (Map.Entry<?,?>) o;             return ONE.equals(entry.getValue())                 && mPositiveSet.contains(entry.getKey());         }         @Override         public boolean isEmpty() {             return mPositiveSet.isEmpty();         }         @Override         public boolean remove(Object o) {             if (!(o instanceof Map.Entry<?,?>))                 return false;             @SuppressWarnings("unchecked") // checked above             Map.Entry<?,?> entry = (Map.Entry<?,?>) o;             return ONE.equals(entry.getValue())                 && mPositiveSet.remove(entry.getKey());         }     }     class EntrySetIterator implements Iterator<Map.Entry<E,Integer>> {         private final Iterator<E> mIterator = mPositiveSet.iterator();;         public boolean hasNext() {             return mIterator.hasNext();         }         public Map.Entry<E,Integer> next() {             return new PositiveMapEntry();         }         public void remove() {             mIterator.remove();         }         class PositiveMapEntry implements Map.Entry<E,Integer> {             private final E mE = mIterator.next();             public E getKey() {                 return mE;             }             public Integer getValue() {                 return ONE;             }             public Integer setValue(Integer value) {                 throw new UnsupportedOperationException();             }             public boolean equals(Object that) {                 if (!(that instanceof Map.Entry<?,?>))                     return false;                 @SuppressWarnings("unchecked") // checked above                     Map.Entry<?,?> e1 = (Map.Entry<?,?>) that;                 Map.Entry<?,?> e2 = this;                 return                     (e1.getKey()==null                      ? e2.getKey()==null                      : e1.getKey().equals(e2.getKey()))                     &&                     (e1.getValue()==null                      ? e2.getValue()==null                      : e1.getValue().equals(e2.getValue()));             }             public int hashCode() {                 return (getKey()==null ? 0 : getKey().hashCode())                     ^ (getValue()==null ? 0 : getValue().hashCode());             }         }     }     /**      * The constant used for the {@code Integer} with value 1.  The      * value is defined by {@code Integer.valueOf(1)}, so may be the      * same object as returned by other calls to {@code valueOf()}.      */     public static final Integer ONE = Integer.valueOf(1);     static final Object[] EMPTY_OBJECT_ARRAY         = new Object[0];   }