Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import java.io.Serializable;
- import java.util.AbstractCollection;
- import java.util.Collection;
- import java.util.ConcurrentModificationException;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Map.Entry;
- /**
- * This class implements a <b>bag</b> or <b>multiset</b> which is an unordered
- * collection that allows duplicates. The <tt>Bag</tt> uses a <tt>HashMap</tt>
- * to keep track of the number of duplicates while allowing generally fast
- * lookup, addition and removal.
- *
- * @param <E>
- * The type of objects maintained by this collection
- * @author Zeriab
- */
- public class Bag<E> extends AbstractCollection<E> implements Collection<E>,
- Cloneable, Serializable {
- private static final long serialVersionUID = -1888153415188000895L;
- // Keeps track of the size for quick size uplook
- private int size;
- // Backing map
- private Map<E, Integer> map;
- /**
- * Counts the number of times the collection have been modified. This is
- * used to make the iterators on the collection fail-fast
- *
- * @see ConcurrentModificationException
- */
- transient int modCount;
- /**
- * Constructs an empty <tt>Bag</tt>. The backing <tt>HashMap</tt> instance
- * has the default initial capacity (16) and the default load factor (0.75).
- */
- public Bag() {
- size = 0;
- map = new HashMap<E, Integer>();
- }
- /**
- * Constructs a new bag containing the elements in the specified collection.
- * The <tt>HashMap</tt> is created with default load factor (0.75) and an
- * initial capacity sufficient to contain the elements in the specified
- * collection.
- *
- * @param c
- * the collection whose elements are to be placed into this bag
- * @throws NullPointerException
- * if the specified collection is null
- */
- public Bag(Collection<? extends E> c) {
- size = 0;
- map = new HashMap<E, Integer>(Math.max((int) (c.size() / .75f) + 1, 16));
- }
- /**
- * Constructs an empty <tt>Bag</tt>. The backing <tt>HashMap</tt> has the
- * specified initial capacity and the default load factor (0.75).
- *
- * @param initialCapacity
- * the initial capacity.
- * @throws IllegalArgumentException
- * if the initial capacity is negative.
- */
- public Bag(int initialCapacity) {
- size = 0;
- map = new HashMap<E, Integer>(initialCapacity);
- }
- /**
- * Constructs an empty <tt>Bag</tt>. The backing <tt>HashMap</tt> has the
- * specified initial capacity and load factor.
- *
- * @param initialCapacity
- * the initial capacity
- * @param loadFactor
- * the load factor
- * @throws IllegalArgumentException
- * if the initial capacity is negative or the load factor is
- * nonpositive
- */
- public Bag(int initialCapacity, float loadFactor) {
- size = 0;
- map = new HashMap<E, Integer>(initialCapacity, loadFactor);
- }
- @Override
- public boolean add(E e) {
- if (map.containsKey(e)) {
- map.put(e, map.get(e) + 1);
- } else {
- map.put(e, 1);
- }
- size++;
- modCount++;
- return true;
- };
- @Override
- public boolean remove(Object o) {
- if (size > 0) {
- // Workaround to get the generic class type
- Class<?> genericClass = map.keySet().iterator().next().getClass();
- if (o.getClass() == genericClass) {
- // We can now safely cast the object
- @SuppressWarnings("unchecked")
- E e = (E) o;
- return removeGeneric(e);
- }
- }
- return false;
- }
- /*
- * Remove an instance of the specified object.
- */
- private boolean removeGeneric(E e) {
- if (map.containsKey(e)) {
- if (map.get(e) <= 1) {
- map.remove(e);
- } else {
- map.put(e, map.get(e) - 1);
- }
- size--;
- modCount++;
- return true;
- }
- return false;
- }
- @Override
- public Iterator<E> iterator() {
- return new BagIterator<E>();
- }
- @Override
- public int size() {
- return size;
- }
- @Override
- public void clear() {
- modCount++;
- map.clear();
- size = 0;
- }
- @Override
- protected Object clone() {
- try {
- // Both fields are clonable and no information needs to be discarded
- // so we can safely rely on the standard clone method
- return super.clone();
- } catch (CloneNotSupportedException e) {
- // Clone is supported
- }
- return null;
- }
- /*
- * A custom iterator is needed for dealing with duplicates
- */
- private final class BagIterator<T> implements Iterator<E> {
- private Iterator<Entry<E, Integer>> entrySetIterator;
- private int count = 0;
- private int max = 0;
- private E current = null;
- private int expectedModCount;
- public BagIterator() {
- entrySetIterator = map.entrySet().iterator();
- expectedModCount = modCount;
- }
- @Override
- public boolean hasNext() {
- if (count < max) {
- return true;
- } else {
- return entrySetIterator.hasNext();
- }
- }
- @Override
- public E next() {
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- if (count < max) {
- count++;
- } else {
- Entry<E, Integer> entrySet = entrySetIterator.next();
- current = entrySet.getKey();
- max = entrySet.getValue();
- count = 1;
- }
- return current;
- }
- @Override
- public void remove() {
- if (current == null)
- throw new IllegalStateException();
- if (modCount != expectedModCount)
- throw new ConcurrentModificationException();
- if (max > 1) {
- Bag.this.remove(current);
- count--;
- } else {
- entrySetIterator.remove();
- size--;
- }
- max--;
- expectedModCount = modCount;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement