AbstractMultiSet.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.collections4.multiset;

  18. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.ObjectOutputStream;
  21. import java.util.AbstractCollection;
  22. import java.util.AbstractSet;
  23. import java.util.Collection;
  24. import java.util.Iterator;
  25. import java.util.Objects;
  26. import java.util.Set;

  27. import org.apache.commons.collections4.IteratorUtils;
  28. import org.apache.commons.collections4.MultiSet;
  29. import org.apache.commons.collections4.Transformer;

  30. /**
  31.  * Abstract implementation of the {@link MultiSet} interface to simplify the
  32.  * creation of subclass implementations.
  33.  *
  34.  * @param <E> the type held in the multiset
  35.  * @since 4.1
  36.  */
  37. public abstract class AbstractMultiSet<E> extends AbstractCollection<E> implements MultiSet<E> {

  38.     /**
  39.      * Inner class AbstractEntry.
  40.      *
  41.      * @param <E> the element type.
  42.      */
  43.     protected abstract static class AbstractEntry<E> implements Entry<E> {

  44.         /**
  45.          * Constructs a new instance.
  46.          */
  47.         public AbstractEntry() {
  48.             // empty
  49.         }

  50.         @Override
  51.         public boolean equals(final Object object) {
  52.             if (object instanceof Entry) {
  53.                 final Entry<?> other = (Entry<?>) object;
  54.                 final E element = getElement();
  55.                 final Object otherElement = other.getElement();

  56.                 return this.getCount() == other.getCount() &&
  57.                        Objects.equals(element, otherElement);
  58.             }
  59.             return false;
  60.         }

  61.         @Override
  62.         public int hashCode() {
  63.             final E element = getElement();
  64.             return (element == null ? 0 : element.hashCode()) ^ getCount();
  65.         }

  66.         @Override
  67.         public String toString() {
  68.             return String.format("%s:%d", getElement(), getCount());
  69.         }
  70.     }

  71.     /**
  72.      * Inner class EntrySet.
  73.      *
  74.      * @param <E> the element type.
  75.      */
  76.     protected static class EntrySet<E> extends AbstractSet<Entry<E>> {

  77.         private final AbstractMultiSet<E> parent;

  78.         /**
  79.          * Constructs a new view of the MultiSet.
  80.          *
  81.          * @param parent  the parent MultiSet
  82.          */
  83.         protected EntrySet(final AbstractMultiSet<E> parent) {
  84.             this.parent = parent;
  85.         }

  86.         @Override
  87.         public boolean contains(final Object obj) {
  88.             if (!(obj instanceof Entry<?>)) {
  89.                 return false;
  90.             }
  91.             final Entry<?> entry = (Entry<?>) obj;
  92.             final Object element = entry.getElement();
  93.             return parent.getCount(element) == entry.getCount();
  94.         }

  95.         @Override
  96.         public Iterator<Entry<E>> iterator() {
  97.             return parent.createEntrySetIterator();
  98.         }

  99.         @Override
  100.         public boolean remove(final Object obj) {
  101.             if (!(obj instanceof Entry<?>)) {
  102.                 return false;
  103.             }
  104.             final Entry<?> entry = (Entry<?>) obj;
  105.             final Object element = entry.getElement();
  106.             if (parent.contains(element)) {
  107.                 final int count = parent.getCount(element);
  108.                 if (entry.getCount() == count) {
  109.                     parent.remove(element, count);
  110.                     return true;
  111.                 }
  112.             }
  113.             return false;
  114.         }

  115.         @Override
  116.         public int size() {
  117.             return parent.uniqueElements();
  118.         }
  119.     }

  120.     /**
  121.      * Inner class iterator for the MultiSet.
  122.      */
  123.     private static final class MultiSetIterator<E> implements Iterator<E> {
  124.         private final AbstractMultiSet<E> parent;
  125.         private final Iterator<Entry<E>> entryIterator;
  126.         private Entry<E> current;
  127.         private int itemCount;
  128.         private boolean canRemove;

  129.         /**
  130.          * Constructs a new instance.
  131.          *
  132.          * @param parent the parent multiset
  133.          */
  134.         MultiSetIterator(final AbstractMultiSet<E> parent) {
  135.             this.parent = parent;
  136.             this.entryIterator = parent.entrySet().iterator();
  137.             this.current = null;
  138.             this.canRemove = false;
  139.         }

  140.         /** {@inheritDoc} */
  141.         @Override
  142.         public boolean hasNext() {
  143.             return itemCount > 0 || entryIterator.hasNext();
  144.         }

  145.         /** {@inheritDoc} */
  146.         @Override
  147.         public E next() {
  148.             if (itemCount == 0) {
  149.                 current = entryIterator.next();
  150.                 itemCount = current.getCount();
  151.             }
  152.             canRemove = true;
  153.             itemCount--;
  154.             return current.getElement();
  155.         }

  156.         /** {@inheritDoc} */
  157.         @Override
  158.         public void remove() {
  159.             if (!canRemove) {
  160.                 throw new IllegalStateException();
  161.             }
  162.             final int count = current.getCount();
  163.             if (count > 1) {
  164.                 parent.remove(current.getElement());
  165.             } else {
  166.                 entryIterator.remove();
  167.             }
  168.             canRemove = false;
  169.         }
  170.     }

  171.     /**
  172.      * Inner class UniqueSet.
  173.      *
  174.      * @param <E> the element type.
  175.      */
  176.     protected static class UniqueSet<E> extends AbstractSet<E> {

  177.         /** The parent multiset */
  178.         protected final AbstractMultiSet<E> parent;

  179.         /**
  180.          * Constructs a new unique element view of the MultiSet.
  181.          *
  182.          * @param parent  the parent MultiSet
  183.          */
  184.         protected UniqueSet(final AbstractMultiSet<E> parent) {
  185.             this.parent = parent;
  186.         }

  187.         @Override
  188.         public void clear() {
  189.             parent.clear();
  190.         }

  191.         @Override
  192.         public boolean contains(final Object key) {
  193.             return parent.contains(key);
  194.         }

  195.         @Override
  196.         public boolean containsAll(final Collection<?> coll) {
  197.             return parent.containsAll(coll);
  198.         }

  199.         @Override
  200.         public Iterator<E> iterator() {
  201.             return parent.createUniqueSetIterator();
  202.         }

  203.         @Override
  204.         public boolean remove(final Object key) {
  205.             return parent.remove(key, parent.getCount(key)) != 0;
  206.         }

  207.         @Override
  208.         public int size() {
  209.             return parent.uniqueElements();
  210.         }
  211.     }

  212.     /** View of the elements */
  213.     private transient Set<E> uniqueSet;

  214.     /** View of the entries */
  215.     private transient Set<Entry<E>> entrySet;

  216.     /**
  217.      * Constructs a new instance subclasses.
  218.      */
  219.     protected AbstractMultiSet() {
  220.     }

  221.     @Override
  222.     public boolean add(final E object) {
  223.         add(object, 1);
  224.         return true;
  225.     }

  226.     @Override
  227.     public int add(final E object, final int occurrences) {
  228.         throw new UnsupportedOperationException();
  229.     }

  230.     /**
  231.      * Clears the multiset removing all elements from the entrySet.
  232.      */
  233.     @Override
  234.     public void clear() {
  235.         final Iterator<Entry<E>> it = entrySet().iterator();
  236.         while (it.hasNext()) {
  237.             it.next();
  238.             it.remove();
  239.         }
  240.     }

  241.     /**
  242.      * Determines if the multiset contains the given element.
  243.      *
  244.      * @param object the object to search for
  245.      * @return true if the multiset contains the given element
  246.      */
  247.     @Override
  248.     public boolean contains(final Object object) {
  249.         return getCount(object) > 0;
  250.     }

  251.     /**
  252.      * Create a new view for the set of entries in this multiset.
  253.      *
  254.      * @return a view of the set of entries
  255.      */
  256.     protected Set<Entry<E>> createEntrySet() {
  257.         return new EntrySet<>(this);
  258.     }

  259.     /**
  260.      * Creates an entry set iterator.
  261.      * Subclasses can override this to return iterators with different properties.
  262.      *
  263.      * @return the entrySet iterator
  264.      */
  265.     protected abstract Iterator<Entry<E>> createEntrySetIterator();

  266.     /**
  267.      * Create a new view for the set of unique elements in this multiset.
  268.      *
  269.      * @return a view of the set of unique elements
  270.      */
  271.     protected Set<E> createUniqueSet() {
  272.         return new UniqueSet<>(this);
  273.     }

  274.     /**
  275.      * Creates a unique set iterator.
  276.      * Subclasses can override this to return iterators with different properties.
  277.      *
  278.      * @return the uniqueSet iterator
  279.      */
  280.     protected Iterator<E> createUniqueSetIterator() {
  281.         final Transformer<Entry<E>, E> transformer = Entry::getElement;
  282.         return IteratorUtils.transformedIterator(entrySet().iterator(), transformer);
  283.     }

  284.     /**
  285.      * Reads the multiset in using a custom routine.
  286.      *
  287.      * @param in the input stream
  288.      * @throws IOException any of the usual I/O related exceptions
  289.      * @throws ClassNotFoundException if the stream contains an object which class cannot be loaded
  290.      * @throws ClassCastException if the stream does not contain the correct objects
  291.      */
  292.     protected void doReadObject(final ObjectInputStream in)
  293.             throws IOException, ClassNotFoundException {
  294.         final int entrySize = in.readInt();
  295.         for (int i = 0; i < entrySize; i++) {
  296.             @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
  297.             final E obj = (E) in.readObject();
  298.             final int count = in.readInt();
  299.             setCount(obj, count);
  300.         }
  301.     }

  302.     /**
  303.      * Writes the multiset out using a custom routine.
  304.      *
  305.      * @param out the output stream
  306.      * @throws IOException any of the usual I/O related exceptions
  307.      */
  308.     protected void doWriteObject(final ObjectOutputStream out) throws IOException {
  309.         out.writeInt(entrySet().size());
  310.         for (final Entry<E> entry : entrySet()) {
  311.             out.writeObject(entry.getElement());
  312.             out.writeInt(entry.getCount());
  313.         }
  314.     }

  315.     /**
  316.      * Returns an unmodifiable view of the entries of this multiset.
  317.      *
  318.      * @return the set of entries in this multiset
  319.      */
  320.     @Override
  321.     public Set<Entry<E>> entrySet() {
  322.         if (entrySet == null) {
  323.             entrySet = createEntrySet();
  324.         }
  325.         return entrySet;
  326.     }

  327.     @Override
  328.     public boolean equals(final Object object) {
  329.         if (object == this) {
  330.             return true;
  331.         }
  332.         if (!(object instanceof MultiSet)) {
  333.             return false;
  334.         }
  335.         final MultiSet<?> other = (MultiSet<?>) object;
  336.         if (other.size() != size()) {
  337.             return false;
  338.         }
  339.         for (final Entry<E> entry : entrySet()) {
  340.             if (other.getCount(entry.getElement()) != getCount(entry.getElement())) {
  341.                 return false;
  342.             }
  343.         }
  344.         return true;
  345.     }

  346.     /**
  347.      * Gets the number of occurrence of the given element in this multiset by
  348.      * iterating over its entrySet.
  349.      *
  350.      * @param object the object to search for
  351.      * @return the number of occurrences of the object, zero if not found
  352.      */
  353.     @Override
  354.     public int getCount(final Object object) {
  355.         for (final Entry<E> entry : entrySet()) {
  356.             final E element = entry.getElement();
  357.             if (Objects.equals(element, object)) {
  358.                 return entry.getCount();
  359.             }
  360.         }
  361.         return 0;
  362.     }

  363.     @Override
  364.     public int hashCode() {
  365.         return entrySet().hashCode();
  366.     }

  367.     /**
  368.      * Gets an iterator over the multiset elements. Elements present in the
  369.      * MultiSet more than once will be returned repeatedly.
  370.      *
  371.      * @return the iterator
  372.      */
  373.     @Override
  374.     public Iterator<E> iterator() {
  375.         return new MultiSetIterator<>(this);
  376.     }

  377.     @Override
  378.     public boolean remove(final Object object) {
  379.         return remove(object, 1) != 0;
  380.     }

  381.     @Override
  382.     public int remove(final Object object, final int occurrences) {
  383.         throw new UnsupportedOperationException();
  384.     }

  385.     @Override
  386.     public boolean removeAll(final Collection<?> coll) {
  387.         boolean result = false;
  388.         for (final Object obj : coll) {
  389.             final boolean changed = remove(obj, getCount(obj)) != 0;
  390.             result = result || changed;
  391.         }
  392.         return result;
  393.     }

  394.     @Override
  395.     public int setCount(final E object, final int count) {
  396.         if (count < 0) {
  397.             throw new IllegalArgumentException("Count must not be negative.");
  398.         }

  399.         final int oldCount = getCount(object);
  400.         if (oldCount < count) {
  401.             add(object, count - oldCount);
  402.         } else {
  403.             remove(object, oldCount - count);
  404.         }
  405.         return oldCount;
  406.     }

  407.     /**
  408.      * Returns the number of elements in this multiset.
  409.      *
  410.      * @return current size of the multiset
  411.      */
  412.     @Override
  413.     public int size() {
  414.         int totalSize = 0;
  415.         for (final Entry<E> entry : entrySet()) {
  416.             totalSize += entry.getCount();
  417.         }
  418.         return totalSize;
  419.     }

  420.     /**
  421.      * Implement a toString() method suitable for debugging.
  422.      *
  423.      * @return a debugging toString
  424.      */
  425.     @Override
  426.     public String toString() {
  427.         return entrySet().toString();
  428.     }

  429.     /**
  430.      * Returns the number of unique elements in this multiset.
  431.      *
  432.      * @return the number of unique elements
  433.      */
  434.     protected abstract int uniqueElements();

  435.     /**
  436.      * Returns a view of the unique elements of this multiset.
  437.      *
  438.      * @return the set of unique elements in this multiset
  439.      */
  440.     @Override
  441.     public Set<E> uniqueSet() {
  442.         if (uniqueSet == null) {
  443.             uniqueSet = createUniqueSet();
  444.         }
  445.         return uniqueSet;
  446.     }

  447. }