View Javadoc

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__ */