Advertisement
Zeriab

Bag example implementation

Dec 30th, 2012
304
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5.51 KB | None | 0 0
  1. import java.io.Serializable;
  2. import java.util.AbstractCollection;
  3. import java.util.Collection;
  4. import java.util.ConcurrentModificationException;
  5. import java.util.HashMap;
  6. import java.util.Iterator;
  7. import java.util.Map;
  8. import java.util.Map.Entry;
  9.  
  10. /**
  11.  * This class implements a <b>bag</b> or <b>multiset</b> which is an unordered
  12.  * collection that allows duplicates. The <tt>Bag</tt> uses a <tt>HashMap</tt>
  13.  * to keep track of the number of duplicates while allowing generally fast
  14.  * lookup, addition and removal.
  15.  *
  16.  * @param <E>
  17.  *            The type of objects maintained by this collection
  18.  * @author Zeriab
  19.  */
  20. public class Bag<E> extends AbstractCollection<E> implements Collection<E>,
  21.         Cloneable, Serializable {
  22.     private static final long serialVersionUID = -1888153415188000895L;
  23.  
  24.     // Keeps track of the size for quick size uplook
  25.     private int size;
  26.     // Backing map
  27.     private Map<E, Integer> map;
  28.  
  29.     /**
  30.      * Counts the number of times the collection have been modified. This is
  31.      * used to make the iterators on the collection fail-fast
  32.      *
  33.      * @see ConcurrentModificationException
  34.      */
  35.     transient int modCount;
  36.  
  37.     /**
  38.      * Constructs an empty <tt>Bag</tt>. The backing <tt>HashMap</tt> instance
  39.      * has the default initial capacity (16) and the default load factor (0.75).
  40.      */
  41.     public Bag() {
  42.         size = 0;
  43.         map = new HashMap<E, Integer>();
  44.     }
  45.  
  46.     /**
  47.      * Constructs a new bag containing the elements in the specified collection.
  48.      * The <tt>HashMap</tt> is created with default load factor (0.75) and an
  49.      * initial capacity sufficient to contain the elements in the specified
  50.      * collection.
  51.      *
  52.      * @param c
  53.      *            the collection whose elements are to be placed into this bag
  54.      * @throws NullPointerException
  55.      *             if the specified collection is null
  56.      */
  57.     public Bag(Collection<? extends E> c) {
  58.         size = 0;
  59.         map = new HashMap<E, Integer>(Math.max((int) (c.size() / .75f) + 1, 16));
  60.     }
  61.  
  62.     /**
  63.      * Constructs an empty <tt>Bag</tt>. The backing <tt>HashMap</tt> has the
  64.      * specified initial capacity and the default load factor (0.75).
  65.      *
  66.      * @param initialCapacity
  67.      *            the initial capacity.
  68.      * @throws IllegalArgumentException
  69.      *             if the initial capacity is negative.
  70.      */
  71.     public Bag(int initialCapacity) {
  72.         size = 0;
  73.         map = new HashMap<E, Integer>(initialCapacity);
  74.     }
  75.  
  76.     /**
  77.      * Constructs an empty <tt>Bag</tt>. The backing <tt>HashMap</tt> has the
  78.      * specified initial capacity and load factor.
  79.      *
  80.      * @param initialCapacity
  81.      *            the initial capacity
  82.      * @param loadFactor
  83.      *            the load factor
  84.      * @throws IllegalArgumentException
  85.      *             if the initial capacity is negative or the load factor is
  86.      *             nonpositive
  87.      */
  88.     public Bag(int initialCapacity, float loadFactor) {
  89.         size = 0;
  90.         map = new HashMap<E, Integer>(initialCapacity, loadFactor);
  91.     }
  92.  
  93.     @Override
  94.     public boolean add(E e) {
  95.         if (map.containsKey(e)) {
  96.             map.put(e, map.get(e) + 1);
  97.         } else {
  98.             map.put(e, 1);
  99.         }
  100.         size++;
  101.         modCount++;
  102.         return true;
  103.     };
  104.  
  105.     @Override
  106.     public boolean remove(Object o) {
  107.         if (size > 0) {
  108.             // Workaround to get the generic class type
  109.             Class<?> genericClass = map.keySet().iterator().next().getClass();
  110.             if (o.getClass() == genericClass) {
  111.                 // We can now safely cast the object
  112.                 @SuppressWarnings("unchecked")
  113.                 E e = (E) o;
  114.                 return removeGeneric(e);
  115.             }
  116.         }
  117.         return false;
  118.     }
  119.  
  120.     /*
  121.      * Remove an instance of the specified object.
  122.      */
  123.     private boolean removeGeneric(E e) {
  124.         if (map.containsKey(e)) {
  125.             if (map.get(e) <= 1) {
  126.                 map.remove(e);
  127.             } else {
  128.                 map.put(e, map.get(e) - 1);
  129.             }
  130.             size--;
  131.             modCount++;
  132.             return true;
  133.         }
  134.         return false;
  135.     }
  136.  
  137.     @Override
  138.     public Iterator<E> iterator() {
  139.         return new BagIterator<E>();
  140.     }
  141.  
  142.     @Override
  143.     public int size() {
  144.         return size;
  145.     }
  146.  
  147.     @Override
  148.     public void clear() {
  149.         modCount++;
  150.         map.clear();
  151.         size = 0;
  152.     }
  153.  
  154.     @Override
  155.     protected Object clone() {
  156.         try {
  157.             // Both fields are clonable and no information needs to be discarded
  158.             // so we can safely rely on the standard clone method
  159.             return super.clone();
  160.         } catch (CloneNotSupportedException e) {
  161.             // Clone is supported
  162.         }
  163.         return null;
  164.     }
  165.  
  166.     /*
  167.      * A custom iterator is needed for dealing with duplicates
  168.      */
  169.     private final class BagIterator<T> implements Iterator<E> {
  170.         private Iterator<Entry<E, Integer>> entrySetIterator;
  171.         private int count = 0;
  172.         private int max = 0;
  173.         private E current = null;
  174.         private int expectedModCount;
  175.  
  176.         public BagIterator() {
  177.             entrySetIterator = map.entrySet().iterator();
  178.             expectedModCount = modCount;
  179.         }
  180.  
  181.         @Override
  182.         public boolean hasNext() {
  183.             if (count < max) {
  184.                 return true;
  185.             } else {
  186.                 return entrySetIterator.hasNext();
  187.             }
  188.         }
  189.  
  190.         @Override
  191.         public E next() {
  192.             if (modCount != expectedModCount)
  193.                 throw new ConcurrentModificationException();
  194.             if (count < max) {
  195.                 count++;
  196.             } else {
  197.                 Entry<E, Integer> entrySet = entrySetIterator.next();
  198.                 current = entrySet.getKey();
  199.                 max = entrySet.getValue();
  200.                 count = 1;
  201.             }
  202.             return current;
  203.         }
  204.  
  205.         @Override
  206.         public void remove() {
  207.             if (current == null)
  208.                 throw new IllegalStateException();
  209.             if (modCount != expectedModCount)
  210.                 throw new ConcurrentModificationException();
  211.             if (max > 1) {
  212.                 Bag.this.remove(current);
  213.                 count--;
  214.             } else {
  215.                 entrySetIterator.remove();
  216.                 size--;
  217.             }
  218.             max--;
  219.             expectedModCount = modCount;
  220.         }
  221.  
  222.     }
  223. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement