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.iterators.AbstractIteratorDecorator; 029import org.apache.commons.collections4.iterators.AbstractListIteratorDecorator; 030import org.apache.commons.collections4.set.UnmodifiableSet; 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 */ 050public class SetUniqueList<E> extends AbstractSerializableListDecorator<E> { 051 052 /** Serialization version. */ 053 private static final long serialVersionUID = 7196982186153478694L; 054 055 /** Internal Set to maintain uniqueness. */ 056 private final Set<E> set; 057 058 /** 059 * Factory method to create a SetList using the supplied list to retain order. 060 * <p> 061 * If the list contains duplicates, these are removed (first indexed one 062 * kept). A <code>HashSet</code> is used for the set behaviour. 063 * 064 * @param <E> the element type 065 * @param list the list to decorate, must not be null 066 * @return a new {@link SetUniqueList} 067 * @throws NullPointerException if list is null 068 * @since 4.0 069 */ 070 public static <E> SetUniqueList<E> setUniqueList(final List<E> list) { 071 if (list == null) { 072 throw new NullPointerException("List must not be null"); 073 } 074 if (list.isEmpty()) { 075 return new SetUniqueList<>(list, new HashSet<E>()); 076 } 077 final List<E> temp = new ArrayList<>(list); 078 list.clear(); 079 final SetUniqueList<E> sl = new SetUniqueList<>(list, new HashSet<E>()); 080 sl.addAll(temp); 081 return sl; 082 } 083 084 // ----------------------------------------------------------------------- 085 /** 086 * Constructor that wraps (not copies) the List and specifies the set to use. 087 * <p> 088 * The set and list must both be correctly initialised to the same elements. 089 * 090 * @param set the set to decorate, must not be null 091 * @param list the list to decorate, must not be null 092 * @throws NullPointerException if set or list is null 093 */ 094 protected SetUniqueList(final List<E> list, final Set<E> set) { 095 super(list); 096 if (set == null) { 097 throw new NullPointerException("Set must not be null"); 098 } 099 this.set = set; 100 } 101 102 // ----------------------------------------------------------------------- 103 /** 104 * Gets an unmodifiable view as a Set. 105 * 106 * @return an unmodifiable set view 107 */ 108 public Set<E> asSet() { 109 return UnmodifiableSet.unmodifiableSet(set); 110 } 111 112 // ----------------------------------------------------------------------- 113 /** 114 * Adds an element to the list if it is not already present. 115 * <p> 116 * <i>(Violation)</i> The <code>List</code> interface requires that this 117 * method returns <code>true</code> always. However this class may return 118 * <code>false</code> because of the <code>Set</code> behaviour. 119 * 120 * @param object the object to add 121 * @return true if object was added 122 */ 123 @Override 124 public boolean add(final E object) { 125 // gets initial size 126 final int sizeBefore = size(); 127 128 // adds element if unique 129 add(size(), object); 130 131 // compares sizes to detect if collection changed 132 return sizeBefore != size(); 133 } 134 135 /** 136 * Adds an element to a specific index in the list if it is not already 137 * present. 138 * <p> 139 * <i>(Violation)</i> The <code>List</code> interface makes the assumption 140 * that the element is always inserted. This may not happen with this 141 * implementation. 142 * 143 * @param index the index to insert at 144 * @param object the object to add 145 */ 146 @Override 147 public void add(final int index, final E object) { 148 // adds element if it is not contained already 149 if (set.contains(object) == false) { 150 super.add(index, object); 151 set.add(object); 152 } 153 } 154 155 /** 156 * Adds a collection of objects to the end of the list avoiding duplicates. 157 * <p> 158 * Only elements that are not already in this list will be added, and 159 * duplicates from the specified collection will be ignored. 160 * <p> 161 * <i>(Violation)</i> The <code>List</code> interface makes the assumption 162 * that the elements are always inserted. This may not happen with this 163 * implementation. 164 * 165 * @param coll the collection to add in iterator order 166 * @return true if this collection changed 167 */ 168 @Override 169 public boolean addAll(final Collection<? extends E> coll) { 170 return addAll(size(), coll); 171 } 172 173 /** 174 * Adds a collection of objects a specific index in the list avoiding 175 * duplicates. 176 * <p> 177 * Only elements that are not already in this list will be added, and 178 * duplicates from the specified collection will be ignored. 179 * <p> 180 * <i>(Violation)</i> The <code>List</code> interface makes the assumption 181 * that the elements are always inserted. This may not happen with this 182 * implementation. 183 * 184 * @param index the index to insert at 185 * @param coll the collection to add in iterator order 186 * @return true if this collection changed 187 */ 188 @Override 189 public boolean addAll(final int index, final Collection<? extends E> coll) { 190 final List<E> temp = new ArrayList<>(); 191 for (final E e : coll) { 192 if (set.add(e)) { 193 temp.add(e); 194 } 195 } 196 return super.addAll(index, temp); 197 } 198 199 // ----------------------------------------------------------------------- 200 /** 201 * Sets the value at the specified index avoiding duplicates. 202 * <p> 203 * The object is set into the specified index. Afterwards, any previous 204 * duplicate is removed. If the object is not already in the list then a 205 * normal set occurs. If it is present, then the old version is removed. 206 * 207 * @param index the index to insert at 208 * @param object the object to set 209 * @return the previous object 210 */ 211 @Override 212 public E set(final int index, final E object) { 213 final int pos = indexOf(object); 214 final E removed = super.set(index, object); 215 216 if (pos != -1 && pos != index) { 217 // the object is already in the unique list 218 // (and it hasn't been swapped with itself) 219 super.remove(pos); // remove the duplicate by index 220 } 221 222 set.remove(removed); // remove the item deleted by the set 223 set.add(object); // add the new item to the unique set 224 225 return removed; // return the item deleted by the set 226 } 227 228 @Override 229 public boolean remove(final Object object) { 230 final boolean result = set.remove(object); 231 if (result) { 232 super.remove(object); 233 } 234 return result; 235 } 236 237 @Override 238 public E remove(final int index) { 239 final E result = super.remove(index); 240 set.remove(result); 241 return result; 242 } 243 244 @Override 245 public boolean removeAll(final Collection<?> coll) { 246 boolean result = false; 247 for (final Object name : coll) { 248 result |= remove(name); 249 } 250 return result; 251 } 252 253 /** 254 * {@inheritDoc} 255 * <p> 256 * This implementation iterates over the elements of this list, checking 257 * each element in turn to see if it's contained in <code>coll</code>. 258 * If it's not contained, it's removed from this list. As a consequence, 259 * it is advised to use a collection type for <code>coll</code> that provides 260 * a fast (e.g. O(1)) implementation of {@link Collection#contains(Object)}. 261 */ 262 @Override 263 public boolean retainAll(final Collection<?> coll) { 264 final boolean result = set.retainAll(coll); 265 if (result == false) { 266 return false; 267 } 268 if (set.size() == 0) { 269 super.clear(); 270 } else { 271 // use the set as parameter for the call to retainAll to improve performance 272 super.retainAll(set); 273 } 274 return result; 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<>(super.iterator(), set); 296 } 297 298 @Override 299 public ListIterator<E> listIterator() { 300 return new SetListListIterator<>(super.listIterator(), set); 301 } 302 303 @Override 304 public ListIterator<E> listIterator(final int index) { 305 return new SetListListIterator<>(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<>(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<>(list.size()); 335 } else { 336 try { 337 subSet = set.getClass().newInstance(); 338 } catch (final InstantiationException ie) { 339 subSet = new HashSet<>(); 340 } catch (final IllegalAccessException iae) { 341 subSet = new HashSet<>(); 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}