Java tutorial
/* * 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]; }