001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.list;
018
019import java.lang.reflect.InvocationTargetException;
020import java.util.ArrayList;
021import java.util.Collection;
022import java.util.HashSet;
023import java.util.Iterator;
024import java.util.List;
025import java.util.ListIterator;
026import java.util.Objects;
027import java.util.Set;
028import java.util.function.Predicate;
029
030import org.apache.commons.collections4.ListUtils;
031import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
032import org.apache.commons.collections4.iterators.AbstractListIteratorDecorator;
033import org.apache.commons.collections4.set.UnmodifiableSet;
034
035/**
036 * Decorates a {@code List} to ensure that no duplicates are present much
037 * like a {@code Set}.
038 * <p>
039 * The {@code List} interface makes certain assumptions/requirements. This
040 * implementation breaks these in certain ways, but this is merely the result of
041 * rejecting duplicates. Each violation is explained in the method, but it
042 * should not affect you. Bear in mind that Sets require immutable objects to
043 * function correctly.
044 * </p>
045 * <p>
046 * The {@link org.apache.commons.collections4.set.ListOrderedSet ListOrderedSet}
047 * class provides an alternative approach, by wrapping an existing Set and
048 * retaining insertion order in the iterator.
049 * </p>
050 * <p>
051 * This class is Serializable from Commons Collections 3.1.
052 * </p>
053 *
054 * @since 3.0
055 */
056public class SetUniqueList<E> extends AbstractSerializableListDecorator<E> {
057
058    /**
059     * Inner class iterator.
060     */
061    static class SetListIterator<E> extends AbstractIteratorDecorator<E> {
062
063        private final Set<E> set;
064        private E last;
065
066        protected SetListIterator(final Iterator<E> it, final Set<E> set) {
067            super(it);
068            this.set = set;
069        }
070
071        @Override
072        public E next() {
073            last = super.next();
074            return last;
075        }
076
077        @Override
078        public void remove() {
079            super.remove();
080            set.remove(last);
081            last = null;
082        }
083    }
084
085    /**
086     * Inner class iterator.
087     */
088    static class SetListListIterator<E> extends
089            AbstractListIteratorDecorator<E> {
090
091        private final Set<E> set;
092        private E last;
093
094        protected SetListListIterator(final ListIterator<E> it, final Set<E> set) {
095            super(it);
096            this.set = set;
097        }
098
099        @Override
100        public void add(final E object) {
101            if (!set.contains(object)) {
102                super.add(object);
103                set.add(object);
104            }
105        }
106
107        @Override
108        public E next() {
109            last = super.next();
110            return last;
111        }
112
113        @Override
114        public E previous() {
115            last = super.previous();
116            return last;
117        }
118
119        @Override
120        public void remove() {
121            super.remove();
122            set.remove(last);
123            last = null;
124        }
125
126        @Override
127        public void set(final E object) {
128            throw new UnsupportedOperationException("ListIterator does not support set");
129        }
130    }
131
132    /** Serialization version. */
133    private static final long serialVersionUID = 7196982186153478694L;
134
135    /**
136     * Factory method to create a SetList using the supplied list to retain order.
137     * <p>
138     * If the list contains duplicates, these are removed (first indexed one
139     * kept). A {@code HashSet} is used for the set behavior.
140     *
141     * @param <E>  the element type
142     * @param list  the list to decorate, must not be null
143     * @return a new {@link SetUniqueList}
144     * @throws NullPointerException if list is null
145     * @since 4.0
146     */
147    public static <E> SetUniqueList<E> setUniqueList(final List<E> list) {
148        Objects.requireNonNull(list, "list");
149        if (list.isEmpty()) {
150            return new SetUniqueList<>(list, new HashSet<>());
151        }
152        final List<E> temp = new ArrayList<>(list);
153        list.clear();
154        final SetUniqueList<E> sl = new SetUniqueList<>(list, new HashSet<>());
155        sl.addAll(temp);
156        return sl;
157    }
158
159    /** Internal Set to maintain uniqueness. */
160    private final Set<E> set;
161
162    /**
163     * Constructor that wraps (not copies) the List and specifies the set to use.
164     * <p>
165     * The set and list must both be correctly initialized to the same elements.
166     *
167     * @param set  the set to decorate, must not be null
168     * @param list  the list to decorate, must not be null
169     * @throws NullPointerException if set or list is null
170     */
171    protected SetUniqueList(final List<E> list, final Set<E> set) {
172        super(list);
173        this.set = Objects.requireNonNull(set, "set");
174    }
175
176    /**
177     * Adds an element to the list if it is not already present.
178     * <p>
179     * <i>(Violation)</i> The {@code List} interface requires that this
180     * method returns {@code true} always. However, this class may return
181     * {@code false} because of the {@code Set} behavior.
182     *
183     * @param object  the object to add
184     * @return true if object was added
185     */
186    @Override
187    public boolean add(final E object) {
188        // gets initial size
189        final int sizeBefore = size();
190
191        // adds element if unique
192        add(size(), object);
193
194        // compares sizes to detect if collection changed
195        return sizeBefore != size();
196    }
197
198    /**
199     * Adds an element to a specific index in the list if it is not already
200     * present.
201     * <p>
202     * <i>(Violation)</i> The {@code List} interface makes the assumption
203     * that the element is always inserted. This may not happen with this
204     * implementation.
205     *
206     * @param index  the index to insert at
207     * @param object  the object to add
208     */
209    @Override
210    public void add(final int index, final E object) {
211        // adds element if it is not contained already
212        if (!set.contains(object)) {
213            set.add(object);
214            super.add(index, object);
215        }
216    }
217
218    /**
219     * Adds a collection of objects to the end of the list avoiding duplicates.
220     * <p>
221     * Only elements that are not already in this list will be added, and
222     * duplicates from the specified collection will be ignored.
223     * <p>
224     * <i>(Violation)</i> The {@code List} interface makes the assumption
225     * that the elements are always inserted. This may not happen with this
226     * implementation.
227     *
228     * @param coll  the collection to add in iterator order
229     * @return true if this collection changed
230     */
231    @Override
232    public boolean addAll(final Collection<? extends E> coll) {
233        return addAll(size(), coll);
234    }
235
236    /**
237     * Adds a collection of objects a specific index in the list avoiding
238     * duplicates.
239     * <p>
240     * Only elements that are not already in this list will be added, and
241     * duplicates from the specified collection will be ignored.
242     * <p>
243     * <i>(Violation)</i> The {@code List} interface makes the assumption
244     * that the elements are always inserted. This may not happen with this
245     * implementation.
246     *
247     * @param index  the index to insert at
248     * @param coll  the collection to add in iterator order
249     * @return true if this collection changed
250     */
251    @Override
252    public boolean addAll(final int index, final Collection<? extends E> coll) {
253        final List<E> temp = new ArrayList<>();
254        for (final E e : coll) {
255            if (set.add(e)) {
256                temp.add(e);
257            }
258        }
259        return super.addAll(index, temp);
260    }
261
262    /**
263     * Gets an unmodifiable view as a Set.
264     *
265     * @return an unmodifiable set view
266     */
267    public Set<E> asSet() {
268        return UnmodifiableSet.unmodifiableSet(set);
269    }
270
271    @Override
272    public void clear() {
273        super.clear();
274        set.clear();
275    }
276
277    @Override
278    public boolean contains(final Object object) {
279        return set.contains(object);
280    }
281
282    @Override
283    public boolean containsAll(final Collection<?> coll) {
284        return set.containsAll(coll);
285    }
286
287    /**
288     * Create a new {@link Set} with the same type as the provided {@code set}
289     * and populate it with all elements of {@code list}.
290     *
291     * @param set  the {@link Set} to be used as return type, must not be null
292     * @param list  the {@link List} to populate the {@link Set}
293     * @return a new {@link Set} populated with all elements of the provided
294     *   {@link List}
295     */
296    protected Set<E> createSetBasedOnList(final Set<E> set, final List<E> list) {
297        Set<E> subSet;
298        if (set.getClass().equals(HashSet.class)) {
299            subSet = new HashSet<>(list.size());
300        } else {
301            try {
302                subSet = set.getClass().getDeclaredConstructor(set.getClass()).newInstance(set);
303            } catch (final InstantiationException
304                    | IllegalAccessException
305                    | InvocationTargetException
306                    | NoSuchMethodException ie) {
307                subSet = new HashSet<>();
308            }
309        }
310        subSet.addAll(list);
311        return subSet;
312    }
313
314    @Override
315    public Iterator<E> iterator() {
316        return new SetListIterator<>(super.iterator(), set);
317    }
318
319    @Override
320    public ListIterator<E> listIterator() {
321        return new SetListListIterator<>(super.listIterator(), set);
322    }
323
324    @Override
325    public ListIterator<E> listIterator(final int index) {
326        return new SetListListIterator<>(super.listIterator(index), set);
327    }
328
329    @Override
330    public E remove(final int index) {
331        final E result = super.remove(index);
332        set.remove(result);
333        return result;
334    }
335
336    @Override
337    public boolean remove(final Object object) {
338        final boolean result = set.remove(object);
339        if (result) {
340            super.remove(object);
341        }
342        return result;
343    }
344
345    @Override
346    public boolean removeAll(final Collection<?> coll) {
347        boolean result = false;
348        for (final Object name : coll) {
349            result |= remove(name);
350        }
351        return result;
352    }
353
354    /**
355     * @since 4.4
356     */
357    @Override
358    public boolean removeIf(final Predicate<? super E> filter) {
359        final boolean result = super.removeIf(filter);
360        set.removeIf(filter);
361        return result;
362    }
363
364    /**
365     * {@inheritDoc}
366     * <p>
367     * This implementation iterates over the elements of this list, checking
368     * each element in turn to see if it's contained in {@code coll}.
369     * If it's not contained, it's removed from this list. As a consequence,
370     * it is advised to use a collection type for {@code coll} that provides
371     * a fast (e.g. O(1)) implementation of {@link Collection#contains(Object)}.
372     */
373    @Override
374    public boolean retainAll(final Collection<?> coll) {
375        final boolean result = set.retainAll(coll);
376        if (!result) {
377            return false;
378        }
379        if (set.isEmpty()) {
380            super.clear();
381        } else {
382            // use the set as parameter for the call to retainAll to improve performance
383            super.retainAll(set);
384        }
385        return result;
386    }
387
388    /**
389     * Sets the value at the specified index avoiding duplicates.
390     * <p>
391     * The object is set into the specified index. Afterwards, any previous
392     * duplicate is removed. If the object is not already in the list then a
393     * normal set occurs. If it is present, then the old version is removed.
394     *
395     * @param index  the index to insert at
396     * @param object  the object to set
397     * @return the previous object
398     */
399    @Override
400    public E set(final int index, final E object) {
401        final int pos = indexOf(object);
402        final E removed = super.set(index, object);
403
404        if (pos != -1 && pos != index) {
405            // the object is already in the unique list
406            // (and it hasn't been swapped with itself)
407            super.remove(pos); // remove the duplicate by index
408        }
409
410        set.remove(removed); // remove the item deleted by the set
411        set.add(object); // add the new item to the unique set
412
413        return removed; // return the item deleted by the set
414    }
415
416    /**
417     * {@inheritDoc}
418     * <p>
419     * NOTE: from 4.0, an unmodifiable list will be returned, as changes to the
420     * subList can invalidate the parent list.
421     */
422    @Override
423    public List<E> subList(final int fromIndex, final int toIndex) {
424        final List<E> superSubList = super.subList(fromIndex, toIndex);
425        final Set<E> subSet = createSetBasedOnList(set, superSubList);
426        return ListUtils.unmodifiableList(new SetUniqueList<>(superSubList, subSet));
427    }
428
429}