SetUniqueList.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.list;

  18. import java.lang.reflect.InvocationTargetException;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.List;
  24. import java.util.ListIterator;
  25. import java.util.Objects;
  26. import java.util.Set;
  27. import java.util.function.Predicate;

  28. import org.apache.commons.collections4.ListUtils;
  29. import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
  30. import org.apache.commons.collections4.iterators.AbstractListIteratorDecorator;
  31. import org.apache.commons.collections4.set.UnmodifiableSet;

  32. /**
  33.  * Decorates a {@code List} to ensure that no duplicates are present much
  34.  * like a {@code Set}.
  35.  * <p>
  36.  * The {@code List} interface makes certain assumptions/requirements. This
  37.  * implementation breaks these in certain ways, but this is merely the result of
  38.  * rejecting duplicates. Each violation is explained in the method, but it
  39.  * should not affect you. Bear in mind that Sets require immutable objects to
  40.  * function correctly.
  41.  * </p>
  42.  * <p>
  43.  * The {@link org.apache.commons.collections4.set.ListOrderedSet ListOrderedSet}
  44.  * class provides an alternative approach, by wrapping an existing Set and
  45.  * retaining insertion order in the iterator.
  46.  * </p>
  47.  * <p>
  48.  * This class is Serializable from Commons Collections 3.1.
  49.  * </p>
  50.  *
  51.  * @param <E> the type of the elements in the list.
  52.  * @since 3.0
  53.  */
  54. public class SetUniqueList<E> extends AbstractSerializableListDecorator<E> {

  55.     /**
  56.      * Inner class iterator.
  57.      */
  58.     static class SetListIterator<E> extends AbstractIteratorDecorator<E> {

  59.         private final Set<E> set;
  60.         private E last;

  61.         protected SetListIterator(final Iterator<E> it, final Set<E> set) {
  62.             super(it);
  63.             this.set = set;
  64.         }

  65.         @Override
  66.         public E next() {
  67.             last = super.next();
  68.             return last;
  69.         }

  70.         @Override
  71.         public void remove() {
  72.             super.remove();
  73.             set.remove(last);
  74.             last = null;
  75.         }
  76.     }

  77.     /**
  78.      * Inner class iterator.
  79.      */
  80.     static class SetListListIterator<E> extends
  81.             AbstractListIteratorDecorator<E> {

  82.         private final Set<E> set;
  83.         private E last;

  84.         protected SetListListIterator(final ListIterator<E> it, final Set<E> set) {
  85.             super(it);
  86.             this.set = set;
  87.         }

  88.         @Override
  89.         public void add(final E object) {
  90.             if (!set.contains(object)) {
  91.                 super.add(object);
  92.                 set.add(object);
  93.             }
  94.         }

  95.         @Override
  96.         public E next() {
  97.             last = super.next();
  98.             return last;
  99.         }

  100.         @Override
  101.         public E previous() {
  102.             last = super.previous();
  103.             return last;
  104.         }

  105.         @Override
  106.         public void remove() {
  107.             super.remove();
  108.             set.remove(last);
  109.             last = null;
  110.         }

  111.         @Override
  112.         public void set(final E object) {
  113.             throw new UnsupportedOperationException("ListIterator does not support set");
  114.         }
  115.     }

  116.     /** Serialization version. */
  117.     private static final long serialVersionUID = 7196982186153478694L;

  118.     /**
  119.      * Factory method to create a SetList using the supplied list to retain order.
  120.      * <p>
  121.      * If the list contains duplicates, these are removed (first indexed one
  122.      * kept). A {@code HashSet} is used for the set behavior.
  123.      *
  124.      * @param <E>  the element type
  125.      * @param list  the list to decorate, must not be null
  126.      * @return a new {@link SetUniqueList}
  127.      * @throws NullPointerException if list is null
  128.      * @since 4.0
  129.      */
  130.     public static <E> SetUniqueList<E> setUniqueList(final List<E> list) {
  131.         Objects.requireNonNull(list, "list");
  132.         if (list.isEmpty()) {
  133.             return new SetUniqueList<>(list, new HashSet<>());
  134.         }
  135.         final List<E> temp = new ArrayList<>(list);
  136.         list.clear();
  137.         final SetUniqueList<E> sl = new SetUniqueList<>(list, new HashSet<>());
  138.         sl.addAll(temp);
  139.         return sl;
  140.     }

  141.     /** Internal Set to maintain uniqueness. */
  142.     private final Set<E> set;

  143.     /**
  144.      * Constructor that wraps (not copies) the List and specifies the set to use.
  145.      * <p>
  146.      * The set and list must both be correctly initialized to the same elements.
  147.      *
  148.      * @param set  the set to decorate, must not be null
  149.      * @param list  the list to decorate, must not be null
  150.      * @throws NullPointerException if set or list is null
  151.      */
  152.     protected SetUniqueList(final List<E> list, final Set<E> set) {
  153.         super(list);
  154.         this.set = Objects.requireNonNull(set, "set");
  155.     }

  156.     /**
  157.      * Adds an element to the list if it is not already present.
  158.      * <p>
  159.      * <em>(Violation)</em> The {@code List} interface requires that this
  160.      * method returns {@code true} always. However, this class may return
  161.      * {@code false} because of the {@code Set} behavior.
  162.      *
  163.      * @param object  the object to add
  164.      * @return true if object was added
  165.      */
  166.     @Override
  167.     public boolean add(final E object) {
  168.         // gets initial size
  169.         final int sizeBefore = size();

  170.         // adds element if unique
  171.         add(size(), object);

  172.         // compares sizes to detect if collection changed
  173.         return sizeBefore != size();
  174.     }

  175.     /**
  176.      * Adds an element to a specific index in the list if it is not already
  177.      * present.
  178.      * <p>
  179.      * <em>(Violation)</em> The {@code List} interface makes the assumption
  180.      * that the element is always inserted. This may not happen with this
  181.      * implementation.
  182.      *
  183.      * @param index  the index to insert at
  184.      * @param object  the object to add
  185.      */
  186.     @Override
  187.     public void add(final int index, final E object) {
  188.         // adds element if it is not contained already
  189.         if (!set.contains(object)) {
  190.             set.add(object);
  191.             super.add(index, object);
  192.         }
  193.     }

  194.     /**
  195.      * Adds a collection of objects to the end of the list avoiding duplicates.
  196.      * <p>
  197.      * Only elements that are not already in this list will be added, and
  198.      * duplicates from the specified collection will be ignored.
  199.      * <p>
  200.      * <em>(Violation)</em> The {@code List} interface makes the assumption
  201.      * that the elements are always inserted. This may not happen with this
  202.      * implementation.
  203.      *
  204.      * @param coll  the collection to add in iterator order
  205.      * @return true if this collection changed
  206.      */
  207.     @Override
  208.     public boolean addAll(final Collection<? extends E> coll) {
  209.         return addAll(size(), coll);
  210.     }

  211.     /**
  212.      * Adds a collection of objects a specific index in the list avoiding
  213.      * duplicates.
  214.      * <p>
  215.      * Only elements that are not already in this list will be added, and
  216.      * duplicates from the specified collection will be ignored.
  217.      * <p>
  218.      * <em>(Violation)</em> The {@code List} interface makes the assumption
  219.      * that the elements are always inserted. This may not happen with this
  220.      * implementation.
  221.      *
  222.      * @param index  the index to insert at
  223.      * @param coll  the collection to add in iterator order
  224.      * @return true if this collection changed
  225.      */
  226.     @Override
  227.     public boolean addAll(final int index, final Collection<? extends E> coll) {
  228.         final List<E> temp = new ArrayList<>();
  229.         for (final E e : coll) {
  230.             if (set.add(e)) {
  231.                 temp.add(e);
  232.             }
  233.         }
  234.         return super.addAll(index, temp);
  235.     }

  236.     /**
  237.      * Gets an unmodifiable view as a Set.
  238.      *
  239.      * @return an unmodifiable set view
  240.      */
  241.     public Set<E> asSet() {
  242.         return UnmodifiableSet.unmodifiableSet(set);
  243.     }

  244.     @Override
  245.     public void clear() {
  246.         super.clear();
  247.         set.clear();
  248.     }

  249.     @Override
  250.     public boolean contains(final Object object) {
  251.         return set.contains(object);
  252.     }

  253.     @Override
  254.     public boolean containsAll(final Collection<?> coll) {
  255.         return set.containsAll(coll);
  256.     }

  257.     /**
  258.      * Create a new {@link Set} with the same type as the provided {@code set}
  259.      * and populate it with all elements of {@code list}.
  260.      *
  261.      * @param set  the {@link Set} to be used as return type, must not be null
  262.      * @param list  the {@link List} to populate the {@link Set}
  263.      * @return a new {@link Set} populated with all elements of the provided
  264.      *   {@link List}
  265.      */
  266.     protected Set<E> createSetBasedOnList(final Set<E> set, final List<E> list) {
  267.         Set<E> subSet;
  268.         if (set.getClass().equals(HashSet.class)) {
  269.             subSet = new HashSet<>(list.size());
  270.         } else {
  271.             try {
  272.                 subSet = set.getClass().getDeclaredConstructor(set.getClass()).newInstance(set);
  273.             } catch (final InstantiationException
  274.                     | IllegalAccessException
  275.                     | InvocationTargetException
  276.                     | NoSuchMethodException ie) {
  277.                 subSet = new HashSet<>();
  278.             }
  279.         }
  280.         subSet.addAll(list);
  281.         return subSet;
  282.     }

  283.     @Override
  284.     public Iterator<E> iterator() {
  285.         return new SetListIterator<>(super.iterator(), set);
  286.     }

  287.     @Override
  288.     public ListIterator<E> listIterator() {
  289.         return new SetListListIterator<>(super.listIterator(), set);
  290.     }

  291.     @Override
  292.     public ListIterator<E> listIterator(final int index) {
  293.         return new SetListListIterator<>(super.listIterator(index), set);
  294.     }

  295.     @Override
  296.     public E remove(final int index) {
  297.         final E result = super.remove(index);
  298.         set.remove(result);
  299.         return result;
  300.     }

  301.     @Override
  302.     public boolean remove(final Object object) {
  303.         final boolean result = set.remove(object);
  304.         if (result) {
  305.             super.remove(object);
  306.         }
  307.         return result;
  308.     }

  309.     @Override
  310.     public boolean removeAll(final Collection<?> coll) {
  311.         boolean result = false;
  312.         for (final Object name : coll) {
  313.             result |= remove(name);
  314.         }
  315.         return result;
  316.     }

  317.     /**
  318.      * @since 4.4
  319.      */
  320.     @Override
  321.     public boolean removeIf(final Predicate<? super E> filter) {
  322.         final boolean result = super.removeIf(filter);
  323.         set.removeIf(filter);
  324.         return result;
  325.     }

  326.     /**
  327.      * {@inheritDoc}
  328.      * <p>
  329.      * This implementation iterates over the elements of this list, checking
  330.      * each element in turn to see if it's contained in {@code coll}.
  331.      * If it's not contained, it's removed from this list. As a consequence,
  332.      * it is advised to use a collection type for {@code coll} that provides
  333.      * a fast (for example O(1)) implementation of {@link Collection#contains(Object)}.
  334.      */
  335.     @Override
  336.     public boolean retainAll(final Collection<?> coll) {
  337.         final boolean result = set.retainAll(coll);
  338.         if (!result) {
  339.             return false;
  340.         }
  341.         if (set.isEmpty()) {
  342.             super.clear();
  343.         } else {
  344.             // use the set as parameter for the call to retainAll to improve performance
  345.             super.retainAll(set);
  346.         }
  347.         return result;
  348.     }

  349.     /**
  350.      * Sets the value at the specified index avoiding duplicates.
  351.      * <p>
  352.      * The object is set into the specified index. Afterwards, any previous
  353.      * duplicate is removed. If the object is not already in the list then a
  354.      * normal set occurs. If it is present, then the old version is removed.
  355.      *
  356.      * @param index  the index to insert at
  357.      * @param object  the object to set
  358.      * @return the previous object
  359.      */
  360.     @Override
  361.     public E set(final int index, final E object) {
  362.         final int pos = indexOf(object);
  363.         final E removed = super.set(index, object);

  364.         if (pos != -1 && pos != index) {
  365.             // the object is already in the unique list
  366.             // (and it hasn't been swapped with itself)
  367.             super.remove(pos); // remove the duplicate by index
  368.         }

  369.         set.remove(removed); // remove the item deleted by the set
  370.         set.add(object); // add the new item to the unique set

  371.         return removed; // return the item deleted by the set
  372.     }

  373.     /**
  374.      * {@inheritDoc}
  375.      * <p>
  376.      * NOTE: from 4.0, an unmodifiable list will be returned, as changes to the
  377.      * subList can invalidate the parent list.
  378.      */
  379.     @Override
  380.     public List<E> subList(final int fromIndex, final int toIndex) {
  381.         final List<E> superSubList = super.subList(fromIndex, toIndex);
  382.         final Set<E> subSet = createSetBasedOnList(set, superSubList);
  383.         return ListUtils.unmodifiableList(new SetUniqueList<>(superSubList, subSet));
  384.     }

  385. }