1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.collections.list;
  17. import java.util.ArrayList;
  18. import java.util.Collection;
  19. import java.util.HashSet;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import java.util.ListIterator;
  23. import java.util.Set;
  24. import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
  25. import org.apache.commons.collections.iterators.AbstractListIteratorDecorator;
  26. import org.apache.commons.collections.set.UnmodifiableSet;
  27. /**
  28. * Decorates a <code>List</code> to ensure that no duplicates are present
  29. * much like a <code>Set</code>.
  30. * <p>
  31. * The <code>List</code> interface makes certain assumptions/requirements.
  32. * This implementation breaks these in certain ways, but this is merely the
  33. * result of rejecting duplicates.
  34. * Each violation is explained in the method, but it should not affect you.
  35. * <p>
  36. * The {@link org.apache.commons.collections.set.ListOrderedSet ListOrderedSet}
  37. * class provides an alternative approach, by wrapping an existing Set and
  38. * retaining insertion order in the iterator.
  39. * <p>
  40. * This class is Serializable from Commons Collections 3.1.
  41. *
  42. * @since Commons Collections 3.0
  43. * @version $Revision: 1.8 $ $Date: 2004/06/03 22:02:13 $
  44. *
  45. * @author Matthew Hawthorne
  46. * @author Stephen Colebourne
  47. */
  48. public class SetUniqueList extends AbstractSerializableListDecorator {
  49. /** Serialization version */
  50. private static final long serialVersionUID = 7196982186153478694L;
  51. /**
  52. * Internal Set to maintain uniqueness.
  53. */
  54. protected final Set set;
  55. /**
  56. * Factory method to create a SetList using the supplied list to retain order.
  57. * <p>
  58. * If the list contains duplicates, these are removed (first indexed one kept).
  59. * A <code>HashSet</code> is used for the set behaviour.
  60. *
  61. * @param list the list to decorate, must not be null
  62. * @throws IllegalArgumentException if list is null
  63. */
  64. public static SetUniqueList decorate(List list) {
  65. if (list == null) {
  66. throw new IllegalArgumentException("List must not be null");
  67. }
  68. if (list.isEmpty()) {
  69. return new SetUniqueList(list, new HashSet());
  70. } else {
  71. List temp = new ArrayList(list);
  72. list.clear();
  73. SetUniqueList sl = new SetUniqueList(list, new HashSet());
  74. sl.addAll(temp);
  75. return sl;
  76. }
  77. }
  78. //-----------------------------------------------------------------------
  79. /**
  80. * Constructor that wraps (not copies) the List and specifies the set to use.
  81. * <p>
  82. * The set and list must both be correctly initialised to the same elements.
  83. *
  84. * @param set the set to decorate, must not be null
  85. * @param list the list to decorate, must not be null
  86. * @throws IllegalArgumentException if set or list is null
  87. */
  88. protected SetUniqueList(List list, Set set) {
  89. super(list);
  90. if (set == null) {
  91. throw new IllegalArgumentException("Set must not be null");
  92. }
  93. this.set = set;
  94. }
  95. //-----------------------------------------------------------------------
  96. /**
  97. * Gets an unmodifiable view as a Set.
  98. *
  99. * @return an unmodifiable set view
  100. */
  101. public Set asSet() {
  102. return UnmodifiableSet.decorate(set);
  103. }
  104. //-----------------------------------------------------------------------
  105. /**
  106. * Adds an element to the list if it is not already present.
  107. * <p>
  108. * <i>(Violation)</i>
  109. * The <code>List</code> interface requires that this method returns
  110. * <code>true</code> always. However this class may return <code>false</code>
  111. * because of the <code>Set</code> behaviour.
  112. *
  113. * @param object the object to add
  114. * @return true if object was added
  115. */
  116. public boolean add(Object object) {
  117. // gets initial size
  118. final int sizeBefore = size();
  119. // adds element if unique
  120. add(size(), object);
  121. // compares sizes to detect if collection changed
  122. return (sizeBefore != size());
  123. }
  124. /**
  125. * Adds an element to a specific index in the list if it is not already present.
  126. * <p>
  127. * <i>(Violation)</i>
  128. * The <code>List</code> interface makes the assumption that the element is
  129. * always inserted. This may not happen with this implementation.
  130. *
  131. * @param index the index to insert at
  132. * @param object the object to add
  133. */
  134. public void add(int index, Object object) {
  135. // adds element if it is not contained already
  136. if (set.contains(object) == false) {
  137. super.add(index, object);
  138. set.add(object);
  139. }
  140. }
  141. /**
  142. * Adds an element to the end of the list if it is not already present.
  143. * <p>
  144. * <i>(Violation)</i>
  145. * The <code>List</code> interface makes the assumption that the element is
  146. * always inserted. This may not happen with this implementation.
  147. *
  148. * @param coll the collection to add
  149. */
  150. public boolean addAll(Collection coll) {
  151. return addAll(size(), coll);
  152. }
  153. /**
  154. * Adds a collection of objects to the end of the list avoiding duplicates.
  155. * <p>
  156. * Only elements that are not already in this list will be added, and
  157. * duplicates from the specified collection will be ignored.
  158. * <p>
  159. * <i>(Violation)</i>
  160. * The <code>List</code> interface makes the assumption that the elements
  161. * are always inserted. This may not happen with this implementation.
  162. *
  163. * @param index the index to insert at
  164. * @param coll the collection to add in iterator order
  165. * @return true if this collection changed
  166. */
  167. public boolean addAll(int index, Collection coll) {
  168. // gets initial size
  169. final int sizeBefore = size();
  170. // adds all elements
  171. for (final Iterator it = coll.iterator(); it.hasNext();) {
  172. add(it.next());
  173. }
  174. // compares sizes to detect if collection changed
  175. return sizeBefore != size();
  176. }
  177. //-----------------------------------------------------------------------
  178. /**
  179. * Sets the value at the specified index avoiding duplicates.
  180. * <p>
  181. * The object is set into the specified index.
  182. * Afterwards, any previous duplicate is removed
  183. * If the object is not already in the list then a normal set occurs.
  184. * If it is present, then the old version is removed and re-added at this index
  185. *
  186. * @param index the index to insert at
  187. * @param object the object to set
  188. * @return the previous object
  189. */
  190. public Object set(int index, Object object) {
  191. int pos = indexOf(object);
  192. Object result = super.set(index, object);
  193. if (pos == -1 || pos == index) {
  194. return result;
  195. }
  196. return remove(pos);
  197. }
  198. public boolean remove(Object object) {
  199. boolean result = super.remove(object);
  200. set.remove(object);
  201. return result;
  202. }
  203. public Object remove(int index) {
  204. Object result = super.remove(index);
  205. set.remove(result);
  206. return result;
  207. }
  208. public boolean removeAll(Collection coll) {
  209. boolean result = super.removeAll(coll);
  210. set.removeAll(coll);
  211. return result;
  212. }
  213. public boolean retainAll(Collection coll) {
  214. boolean result = super.retainAll(coll);
  215. set.retainAll(coll);
  216. return result;
  217. }
  218. public void clear() {
  219. super.clear();
  220. set.clear();
  221. }
  222. public boolean contains(Object object) {
  223. return set.contains(object);
  224. }
  225. public boolean containsAll(Collection coll) {
  226. return set.containsAll(coll);
  227. }
  228. public Iterator iterator() {
  229. return new SetListIterator(super.iterator(), set);
  230. }
  231. public ListIterator listIterator() {
  232. return new SetListListIterator(super.listIterator(), set);
  233. }
  234. public ListIterator listIterator(int index) {
  235. return new SetListListIterator(super.listIterator(index), set);
  236. }
  237. public List subList(int fromIndex, int toIndex) {
  238. return new SetUniqueList(super.subList(fromIndex, toIndex), set);
  239. }
  240. //-----------------------------------------------------------------------
  241. /**
  242. * Inner class iterator.
  243. */
  244. static class SetListIterator extends AbstractIteratorDecorator {
  245. protected final Set set;
  246. protected Object last = null;
  247. protected SetListIterator(Iterator it, Set set) {
  248. super(it);
  249. this.set = set;
  250. }
  251. public Object next() {
  252. last = super.next();
  253. return last;
  254. }
  255. public void remove() {
  256. super.remove();
  257. set.remove(last);
  258. last = null;
  259. }
  260. }
  261. /**
  262. * Inner class iterator.
  263. */
  264. static class SetListListIterator extends AbstractListIteratorDecorator {
  265. protected final Set set;
  266. protected Object last = null;
  267. protected SetListListIterator(ListIterator it, Set set) {
  268. super(it);
  269. this.set = set;
  270. }
  271. public Object next() {
  272. last = super.next();
  273. return last;
  274. }
  275. public Object previous() {
  276. last = super.previous();
  277. return last;
  278. }
  279. public void remove() {
  280. super.remove();
  281. set.remove(last);
  282. last = null;
  283. }
  284. public void add(Object object) {
  285. if (set.contains(object) == false) {
  286. super.add(object);
  287. set.add(object);
  288. }
  289. }
  290. public void set(Object object) {
  291. throw new UnsupportedOperationException("ListIterator does not support set");
  292. }
  293. }
  294. }