47
47
import javax .annotation .concurrent .ThreadSafe ;
48
48
import java .util .NoSuchElementException ;
49
49
50
+ /**
51
+ * A mutable class implementing the SplitableIterator interface in a reusable way.
52
+ * Maintains a current position in the collection it iterates over and delegates
53
+ * to a "state" object provided by the collection to move forward inside the collection.
54
+ * To implement spliterator functionality for parallel streams it limits itself to
55
+ * a specific subset of the collection by way of an offset (current position) and
56
+ * limit (stopping position).
57
+ */
50
58
@ ThreadSafe
51
59
public class GenericIterator <T >
52
60
extends AbstractSplitableIterator <T >
53
61
{
54
62
static final int MIN_SIZE_FOR_SPLIT = 32 ;
55
63
56
- private final Iterable <T > root ;
57
- private final int limit ;
58
- private int offset ;
59
- private boolean uninitialized ;
60
- private State <T > state ;
64
+ private final Iterable <T > root ; // the collection we are iterating over
65
+ private final int limit ; // stopping position in our allowed range
66
+ private int offset ; // current position in our allowed range
67
+ private boolean uninitialized ; // true until next or hasNext has been called first time
68
+ private State <T > state ; // tracks the next available position in the collection
61
69
62
70
public GenericIterator (@ Nonnull Iterable <T > root ,
63
71
int offset ,
@@ -70,9 +78,24 @@ public GenericIterator(@Nonnull Iterable<T> root,
70
78
uninitialized = true ;
71
79
}
72
80
81
+ /**
82
+ * Interface for collections that can be iterated over by GenericIterator using State objects
83
+ * to track the progress of the iteration. These do not need to be thread safe.
84
+ */
73
85
public interface Iterable <T >
74
86
extends SplitableIterable <T >
75
87
{
88
+ /**
89
+ * Create a State to iterate over the specified range of values if possible.
90
+ * Can return null if no more values are available. The range must be valid
91
+ * for the collection being iterated over. The returned State will return the
92
+ * parent as its result when iteration over the range is completed.
93
+ *
94
+ * @param parent State to return to once the request iteration completes
95
+ * @param offset starting point for iteration
96
+ * @param limit stopping point for iteration
97
+ * @return null or a valid state
98
+ */
76
99
@ Nullable
77
100
State <T > iterateOverRange (@ Nullable State <T > parent ,
78
101
int offset ,
@@ -107,6 +130,13 @@ public int getSpliteratorCharacteristics()
107
130
}
108
131
}
109
132
133
+ /**
134
+ * Interface for objects that track the current position of an iteration in progress.
135
+ * Unlike an Iterator which "looks ahead" to the next position (hasNext/next) a
136
+ * State object "lives in the moment" and knows the current position (hasValue/value)
137
+ * and doesn't know if a next value exists until told to go there (advance).
138
+ * These do not need to be thread safe.
139
+ */
110
140
public interface State <T >
111
141
{
112
142
default boolean hasValue ()
@@ -119,19 +149,26 @@ default T value()
119
149
throw new NoSuchElementException ();
120
150
}
121
151
152
+ /**
153
+ * Try to move forward to the next position. Returns either a valid state (if next position exists)
154
+ * or null (if there is no next position). The returned State might be this State object or a new
155
+ * State, or null. The returned State might have a value or it might be empty.
156
+ */
122
157
State <T > advance ();
123
158
}
124
159
125
160
@ Override
126
161
public synchronized boolean hasNext ()
127
162
{
128
- return prepare ();
163
+ advanceStateToStartingPositionIfNecessary ();
164
+ return stateHasValue ();
129
165
}
130
166
131
167
@ Override
132
168
public synchronized T next ()
133
169
{
134
- if (!prepare ()) {
170
+ advanceStateToStartingPositionIfNecessary ();
171
+ if (!stateHasValue ()) {
135
172
throw new NoSuchElementException ();
136
173
}
137
174
final T answer = state .value ();
@@ -160,28 +197,40 @@ public synchronized SplitIterator<T> splitIterator()
160
197
new GenericIterator <>(root , splitIndex , limit ));
161
198
}
162
199
163
- private boolean prepare ()
200
+ /**
201
+ * Ensures that state is on a valid position if possible. Gets a starting state if we are
202
+ * starting a new iteration otherwise it calls advance if necessary to move to the first
203
+ * available position.
204
+ */
205
+ private void advanceStateToStartingPositionIfNecessary ()
164
206
{
165
207
if (uninitialized ) {
166
208
state = root .iterateOverRange (null , offset , limit );
167
209
uninitialized = false ;
168
210
}
169
- while (state != null ) {
170
- if (state .hasValue ()) {
171
- return true ;
172
- }
211
+ while (state != null && !state .hasValue ()) {
173
212
state = state .advance ();
174
213
}
175
- return false ;
176
214
}
177
215
178
- public static <T > State <T > valueState (State <T > parent ,
179
- T value )
216
+ private boolean stateHasValue ()
217
+ {
218
+ return state != null && state .hasValue ();
219
+ }
220
+
221
+ /**
222
+ * Returns a State for iterating a single value.
223
+ */
224
+ public static <T > State <T > singleValueState (State <T > parent ,
225
+ T value )
180
226
{
181
227
return new SingleValueState <>(parent , value );
182
228
}
183
229
184
- public static <T > Iterable <T > valueIterable (T value )
230
+ /**
231
+ * Returns an Iterable for iterating a single value.
232
+ */
233
+ public static <T > Iterable <T > singleValueIterable (T value )
185
234
{
186
235
return new Iterable <T >()
187
236
{
@@ -194,7 +243,7 @@ public State<T> iterateOverRange(@Nullable State<T> parent,
194
243
if (offset == limit ) {
195
244
return parent ;
196
245
} else {
197
- return new SingleValueState <T >(parent , value );
246
+ return new SingleValueState <>(parent , value );
198
247
}
199
248
}
200
249
@@ -206,6 +255,9 @@ public int iterableSize()
206
255
};
207
256
}
208
257
258
+ /**
259
+ * Returns a State for iterating multiple values stored in an Indexed collection.
260
+ */
209
261
public static <T > State <T > multiValueState (@ Nullable State <T > parent ,
210
262
@ Nonnull Indexed <T > values ,
211
263
int offset ,
@@ -215,22 +267,30 @@ public static <T> State<T> multiValueState(@Nullable State<T> parent,
215
267
return new MultiValueState <>(parent , values , offset , limit );
216
268
}
217
269
218
- public static <T > State <T > indexedState (State <T > parent ,
219
- Indexed <? extends Iterable <T >> children ,
220
- int offset ,
221
- int limit )
270
+ /**
271
+ * Returns a State for iterating multiple collections (Iterables) that are themselves
272
+ * stored in an Indexed collection.
273
+ */
274
+ public static <T > State <T > multiIterableState (@ Nullable State <T > parent ,
275
+ @ Nonnull Indexed <? extends Iterable <T >> children ,
276
+ int offset ,
277
+ int limit )
222
278
{
223
279
assert 0 <= offset && offset <= limit ;
224
280
if (offset == limit ) {
225
281
return parent ;
226
282
} else {
227
- return new IndexedState <>(parent , children , offset , limit );
283
+ return new MultiIterableState <>(parent , children , offset , limit );
228
284
}
229
285
}
230
286
231
- public static <A , B > State <B > transformState (State <B > parent ,
232
- State <A > source ,
233
- Func1 <A , B > transforminator )
287
+ /**
288
+ * Returns a State for iterating over another State's values but transforming each of
289
+ * those values using a function before returning to its caller.
290
+ */
291
+ public static <A , B > State <B > transformState (@ Nullable State <B > parent ,
292
+ @ Nullable State <A > source ,
293
+ @ Nonnull Func1 <A , B > transforminator )
234
294
{
235
295
if (source == null ) {
236
296
return parent ;
@@ -239,6 +299,10 @@ public static <A, B> State<B> transformState(State<B> parent,
239
299
}
240
300
}
241
301
302
+ /**
303
+ * Returns an Iterable for iterating over another Iterable's values but transforming each of
304
+ * those values using a function before returning to its caller.
305
+ */
242
306
public static <A , B > Iterable <B > transformIterable (@ Nonnull Iterable <A > source ,
243
307
@ Nonnull Func1 <A , B > transforminator )
244
308
{
@@ -268,7 +332,7 @@ private static class SingleValueState<T>
268
332
private final T value ;
269
333
private boolean available ;
270
334
271
- private SingleValueState (State <T > parent ,
335
+ private SingleValueState (@ Nullable State <T > parent ,
272
336
T value )
273
337
{
274
338
this .parent = parent ;
@@ -342,22 +406,22 @@ public GenericIterator.State<T> advance()
342
406
}
343
407
}
344
408
345
- private static class IndexedState <T >
409
+ private static class MultiIterableState <T >
346
410
implements State <T >
347
411
{
348
412
private final State <T > parent ;
349
- private final Indexed <? extends Iterable <T >> children ;
413
+ private final Indexed <? extends Iterable <T >> collections ;
350
414
private int offset ;
351
415
private int limit ;
352
416
private int index ;
353
417
354
- public IndexedState ( State <T > parent ,
355
- Indexed <? extends Iterable <T >> children ,
356
- int offset ,
357
- int limit )
418
+ private MultiIterableState ( @ Nullable State <T > parent ,
419
+ @ Nonnull Indexed <? extends Iterable <T >> collections ,
420
+ int offset ,
421
+ int limit )
358
422
{
359
423
this .parent = parent ;
360
- this .children = children ;
424
+ this .collections = collections ;
361
425
this .limit = limit ;
362
426
this .offset = offset ;
363
427
index = 0 ;
@@ -366,17 +430,25 @@ public IndexedState(State<T> parent,
366
430
@ Override
367
431
public State <T > advance ()
368
432
{
369
- final Iterable <T > child = children .get (index );
370
- final int size = child .iterableSize ();
433
+ final Iterable <T > collection = collections .get (index );
434
+ final int size = collection .iterableSize ();
371
435
if (offset >= size ) {
436
+ // Starting point for iteration is somewhere beyond this collection.
437
+ // Advance to the next collection to try again. Offset and limit have
438
+ // to be adjusted accordingly for the next collection.
372
439
index += 1 ;
373
440
offset -= size ;
374
441
limit -= size ;
375
442
return this ;
376
443
} else if (limit <= size ) {
377
- return child .iterateOverRange (parent , offset , limit );
444
+ // this collection contains all remaining values so pass control to it forever
445
+ return collection .iterateOverRange (parent , offset , limit );
378
446
} else {
379
- final State <T > answer = child .iterateOverRange (this , offset , size );
447
+ // This collection contains at least one value. Transfer control to it
448
+ // passing us as parent so we can resume control once it's exhausted.
449
+ // Offset and limit have to be adjusted to resume at the next collection
450
+ // when we get control again.
451
+ final State <T > answer = collection .iterateOverRange (this , offset , size );
380
452
index += 1 ;
381
453
offset = 0 ;
382
454
limit -= size ;
0 commit comments