1 /*
2 * Copyright (c) 2003-2008 by Cosylab d. d.
3 *
4 * This file is part of Java-Common.
5 *
6 * Java-Common is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Java-Common is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with Java-Common. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package com.cosylab.util;
21
22 import java.util.HashSet;
23 import java.util.Iterator;
24
25 /**
26 * Utilities for working with iterators.
27 *
28 * @author Klemen Zagar, Cosylab
29 */
30 public class Iterators {
31
32 /**
33 * Determine whether two iterators iterate through the same set of elements.
34 * If the same element appears more than once in the set, it is treated as
35 * if it appeared exactly once.
36 *
37 * <p>
38 * <b>Note:</b> This implementation is not efficient for iterators over
39 * large sets, as it first puts elements of both iterators in two sets, and
40 * then compares the sets.
41 * </p>
42 *
43 * @param iterA
44 * First iterator.
45 * @param iterB
46 * Second iterator.
47 * @return <code>true</code> if elements are the same.
48 */
49 public static boolean areEqual(Iterator<? extends Object> iterA,
50 Iterator<? extends Object> iterB) {
51 if (iterA == null || iterB == null) {
52 throw new IllegalArgumentException();
53 }
54
55 HashSet<Object> setA = new HashSet<Object>();
56 HashSet<Object> setB = new HashSet<Object>();
57
58 // First, we put elements of the iterators in a set.
59 while (iterA.hasNext()) {
60 setA.add(iterA.next());
61 }
62 while (iterB.hasNext()) {
63 setB.add(iterB.next());
64 }
65
66 // If the sets aren't of equal size, they can't possibly contain
67 // the same elements.
68 if (setA.size() != setB.size()) {
69 return false;
70 }
71
72 // Check that all elements of the first set are in the second set.
73 iterA = setA.iterator();
74 while (iterA.hasNext()) {
75 if (!setB.contains(iterA.next())) {
76 return false;
77 }
78 }
79
80 return true;
81 }
82
83 /**
84 * Do two iterators iterate through the same elements in the same order?
85 *
86 * @param iterA
87 * First iterator.
88 * @param iterB
89 * Second iterator.
90 * @return <code>true</code> if the iteration order is the same.
91 */
92 public static boolean areEqualOrder(Iterator<? extends Object> iterA,
93 Iterator<? extends Object> iterB) {
94 if (iterA == null || iterB == null) {
95 throw new IllegalArgumentException();
96 }
97
98 while (iterA.hasNext()) {
99 if (!iterB.hasNext()) {
100 return false;
101 }
102
103 Object a = iterA.next();
104 Object b = iterB.next();
105
106 if (a == null) {
107 if (b != null) {
108 return false;
109 }
110 }
111
112 if (!a.equals(b)) {
113 return false;
114 }
115 }
116
117 if (iterB.hasNext()) {
118 return false;
119 }
120
121 return true;
122 }
123
124 /**
125 * Intersection of two iterators.
126 *
127 * <p>
128 * <b>Note:</b> the implementation is such that the first iterator (<code>iterA</code>)
129 * is first traversed in its entirety. Whenever possible, <code>iterA</code>
130 * should be the <em>shorter</em> of the two iterators.
131 * </p>
132 *
133 * @param <T>
134 * Type across which the iterator iterates.
135 * @param iterA
136 * First iterator.
137 * @param iterB
138 * Second iterator.
139 * @return Iterator through items traversed both by <code>iterA</code> and
140 * <code>iterB</code>.
141 */
142 public static <T> Iterator<T> intersect(Iterator<T> iterA, Iterator<T> iterB) {
143 final HashSet<Object> setA = new HashSet<Object>();
144 final Iterator<T> iterBFinal = iterB;
145 while (iterA.hasNext()) {
146 setA.add(iterA.next());
147 }
148 return new Iterator<T>() {
149 private T current;
150
151 {
152 moveToNext();
153 }
154
155 private void moveToNext() {
156 T tmp = null;
157 current = null;
158 while (iterBFinal.hasNext()) {
159 tmp = iterBFinal.next();
160 if (setA.contains(tmp)) {
161 current = tmp;
162 return;
163 }
164 }
165 }
166
167 public boolean hasNext() {
168 return current != null;
169 }
170
171 public T next() {
172 T result = current;
173 current = null;
174 moveToNext();
175 return result;
176 }
177
178 public void remove() {
179 throw new UnsupportedOperationException();
180 }
181 };
182 }
183
184 /**
185 * Create an iterator that iterates only through distinct values.
186 *
187 * @param <T>
188 * Type across which the iterator is iterating.
189 * @param iter
190 * The underlying iterator.
191 * @return The iterator that iterates only through distinct values.
192 */
193 public static <T> Iterator<T> distinct(final Iterator<T> iter) {
194 return new Iterator<T>() {
195 // Elements that were already returned.
196 private HashSet<T> elements = new HashSet<T>();
197
198 // The value to return next.
199 private T next = null;
200
201 // Instance initializer (constructor).
202 {
203 // Advance to the first element.
204 goToNext();
205 }
206
207 /*
208 * (non-Javadoc)
209 * @see java.util.Iterator#hasNext()
210 */
211 public boolean hasNext() {
212 return next != null;
213 }
214
215 /*
216 * (non-Javadoc)
217 * @see java.util.Iterator#next()
218 */
219 public T next() {
220 T result = next;
221 goToNext();
222 return result;
223 }
224
225 /*
226 * (non-Javadoc)
227 * @see java.util.Iterator#remove()
228 */
229 public void remove() {
230 throw new UnsupportedOperationException();
231 }
232
233 /**
234 * Advance to the next element.
235 */
236 private void goToNext() {
237 while (iter.hasNext()) {
238 next = iter.next();
239 if (!elements.contains(next)) {
240 elements.add(next);
241 return;
242 }
243 }
244 next = null;
245 }
246 };
247 }
248 }
249
250 /* __oOo__ */