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