0
# Iterator and Size Operations
1
2
Enhanced iterator methods and efficient size comparison utilities for collections.
3
4
## Iterator Extensions
5
6
### IteratorExtensionMethods
7
8
```scala { .api }
9
implicit class IteratorExtensionMethods[A](private val self: Iterator[A]) extends AnyVal {
10
def nextOption(): Option[A]
11
def sameElements[B >: A](that: TraversableOnce[B]): Boolean
12
def concat[B >: A](that: TraversableOnce[B]): TraversableOnce[B]
13
def tapEach[U](f: A => U): Iterator[A]
14
}
15
```
16
17
#### nextOption
18
19
```scala { .api }
20
def nextOption(): Option[A]
21
```
22
23
Safe version of `next()` that returns `None` when the iterator is exhausted instead of throwing an exception.
24
25
**Usage:**
26
```scala
27
val iterator = List(1, 2, 3).iterator
28
29
iterator.nextOption() // Some(1)
30
iterator.nextOption() // Some(2)
31
iterator.nextOption() // Some(3)
32
iterator.nextOption() // None (instead of NoSuchElementException)
33
```
34
35
#### sameElements
36
37
```scala { .api }
38
def sameElements[B >: A](that: TraversableOnce[B]): Boolean
39
```
40
41
Compare two iterators/traversables element by element for equality.
42
43
**Usage:**
44
```scala
45
val iter1 = List(1, 2, 3).iterator
46
val iter2 = Vector(1, 2, 3).iterator
47
val iter3 = List(1, 2, 4).iterator
48
49
iter1.sameElements(iter2) // true
50
iter1.sameElements(iter3) // false
51
52
// Works with any TraversableOnce
53
val list = List(1, 2, 3)
54
list.iterator.sameElements(list) // true
55
```
56
57
#### concat
58
59
```scala { .api }
60
def concat[B >: A](that: TraversableOnce[B]): TraversableOnce[B]
61
```
62
63
Concatenate the iterator with another `TraversableOnce`.
64
65
**Usage:**
66
```scala
67
val iter1 = List(1, 2, 3).iterator
68
val iter2 = List(4, 5, 6)
69
70
val combined = iter1.concat(iter2)
71
combined.toList // List(1, 2, 3, 4, 5, 6)
72
```
73
74
#### tapEach
75
76
```scala { .api }
77
def tapEach[U](f: A => U): Iterator[A]
78
```
79
80
Apply a function to each element for side effects while preserving the iterator.
81
82
**Usage:**
83
```scala
84
val iterator = List(1, 2, 3, 4, 5).iterator
85
86
val processed = iterator
87
.tapEach(x => println(s"Processing: $x"))
88
.filter(_ % 2 == 0)
89
.tapEach(x => println(s"Even number: $x"))
90
91
processed.toList
92
// Output:
93
// Processing: 1
94
// Processing: 2
95
// Even number: 2
96
// Processing: 3
97
// Processing: 4
98
// Even number: 4
99
// Processing: 5
100
// Result: List(2, 4)
101
```
102
103
## Size Comparison Operations
104
105
### SizeCompareOps
106
107
```scala { .api }
108
class SizeCompareOps(private val it: Traversable[_]) extends AnyVal {
109
def <(size: Int): Boolean
110
def <=(size: Int): Boolean
111
def ==(size: Int): Boolean
112
def !=(size: Int): Boolean
113
def >=(size: Int): Boolean
114
def >(size: Int): Boolean
115
}
116
```
117
118
Efficient size comparison operations that can short-circuit for better performance, especially with large or infinite collections.
119
120
#### Size Comparison Methods
121
122
```scala { .api }
123
def <(size: Int): Boolean // Tests if collection size is less than given size
124
def <=(size: Int): Boolean // Tests if collection size is less than or equal to given size
125
def ==(size: Int): Boolean // Tests if collection size equals given size
126
def !=(size: Int): Boolean // Tests if collection size is not equal to given size
127
def >=(size: Int): Boolean // Tests if collection size is greater than or equal to given size
128
def >(size: Int): Boolean // Tests if collection size is greater than given size
129
```
130
131
**Usage:**
132
```scala
133
import scala.collection.compat._
134
135
val list = List(1, 2, 3, 4, 5)
136
137
// Size comparisons - more efficient than list.size comparisons
138
list.sizeIs < 10 // true
139
list.sizeIs <= 5 // true
140
list.sizeIs == 5 // true
141
list.sizeIs != 3 // true
142
list.sizeIs >= 5 // true
143
list.sizeIs > 2 // true
144
145
// Works with any Traversable
146
val set = Set("a", "b", "c")
147
set.sizeIs == 3 // true
148
set.sizeIs > 5 // false
149
```
150
151
### TraversableExtensionMethods
152
153
```scala { .api }
154
implicit class TraversableExtensionMethods[A](private val self: Traversable[A])
155
extends AnyVal {
156
def iterableFactory: GenericCompanion[Traversable]
157
def sizeCompare(otherSize: Int): Int
158
def sizeIs: SizeCompareOps
159
def sizeCompare(that: Traversable[_]): Int
160
}
161
```
162
163
#### sizeCompare
164
165
```scala { .api }
166
def sizeCompare(otherSize: Int): Int
167
def sizeCompare(that: Traversable[_]): Int
168
```
169
170
Compare collection size with an integer or another collection, returning negative/zero/positive like `Ordering`.
171
172
**Returns:**
173
- Negative value if the collection is smaller
174
- Zero if sizes are equal
175
- Positive value if the collection is larger
176
177
**Usage:**
178
```scala
179
val list1 = List(1, 2, 3)
180
val list2 = List(4, 5)
181
182
list1.sizeCompare(5) // negative (3 < 5)
183
list1.sizeCompare(3) // zero (3 == 3)
184
list1.sizeCompare(2) // positive (3 > 2)
185
186
list1.sizeCompare(list2) // positive (3 > 2)
187
list2.sizeCompare(list1) // negative (2 < 3)
188
```
189
190
#### sizeIs
191
192
```scala { .api }
193
def sizeIs: SizeCompareOps
194
```
195
196
Get a `SizeCompareOps` instance for the collection to perform size comparisons.
197
198
### SeqExtensionMethods
199
200
```scala { .api }
201
implicit class SeqExtensionMethods[A](private val self: Seq[A]) extends AnyVal {
202
def lengthIs: SizeCompareOps
203
}
204
```
205
206
#### lengthIs
207
208
```scala { .api }
209
def lengthIs: SizeCompareOps
210
```
211
212
Size comparison operations specifically for sequences (using length terminology).
213
214
**Usage:**
215
```scala
216
val seq = Seq(1, 2, 3, 4)
217
218
seq.lengthIs < 10 // true
219
seq.lengthIs == 4 // true
220
seq.lengthIs > 5 // false
221
```
222
223
## Performance Benefits
224
225
### Short-Circuit Evaluation
226
227
Size comparison operations can short-circuit, providing better performance than computing the full size:
228
229
```scala
230
val largeList = (1 to 1000000).toList
231
232
// Inefficient - computes full size
233
largeList.size > 100 // Processes all 1M elements
234
235
// Efficient - stops after 101 elements
236
largeList.sizeIs > 100 // Only processes 101 elements
237
```
238
239
### Lazy Collections
240
241
Size operations work efficiently with lazy collections:
242
243
```scala
244
import scala.collection.compat.immutable._
245
246
val infiniteStream = LazyList.from(1)
247
248
// These operations terminate quickly
249
infiniteStream.sizeIs > 5 // true (checks first 6 elements)
250
infiniteStream.sizeIs == 10 // false (checks first 11 elements)
251
252
// This would never terminate
253
// infiniteStream.size // Don't do this!
254
```
255
256
## Complete Usage Examples
257
258
### Processing Large Datasets
259
260
```scala
261
import scala.collection.compat._
262
263
def processDataBatch[T](data: Iterable[T], maxBatchSize: Int): List[List[T]] = {
264
if (data.sizeIs <= maxBatchSize) {
265
List(data.toList)
266
} else {
267
data.grouped(maxBatchSize).toList
268
}
269
}
270
271
// Efficient - stops counting after maxBatchSize + 1 elements
272
val batches = processDataBatch(largeDataset, 1000)
273
```
274
275
### Validation and Constraints
276
277
```scala
278
def validateCollectionSizes[T](
279
required: Traversable[T],
280
optional: Traversable[T]
281
): Either[String, Unit] = {
282
283
if (required.sizeIs == 0) {
284
Left("Required items cannot be empty")
285
} else if (required.sizeIs > 100) {
286
Left("Too many required items (max 100)")
287
} else if (optional.sizeIs > 50) {
288
Left("Too many optional items (max 50)")
289
} else {
290
Right(())
291
}
292
}
293
```
294
295
### Iterator Processing with Debugging
296
297
```scala
298
def processUserData(users: Iterator[User]): List[ProcessedUser] = {
299
users
300
.tapEach(user => println(s"Processing user: ${user.id}"))
301
.filter(_.isActive)
302
.tapEach(user => println(s"Active user: ${user.id}"))
303
.take(100) // Limit processing
304
.tapEach(user => println(s"Within limit: ${user.id}"))
305
.map(processUser)
306
.toList
307
}
308
309
case class User(id: String, isActive: Boolean, name: String)
310
case class ProcessedUser(id: String, name: String, processedAt: Long)
311
312
def processUser(user: User): ProcessedUser =
313
ProcessedUser(user.id, user.name, System.currentTimeMillis())
314
```
315
316
### Safe Iterator Consumption
317
318
```scala
319
def safeIteratorProcessing[T](iterator: Iterator[T]): List[T] = {
320
val buffer = scala.collection.mutable.ListBuffer[T]()
321
322
while (iterator.hasNext) {
323
iterator.nextOption() match {
324
case Some(element) => buffer += element
325
case None => // Iterator was exhausted between hasNext and next
326
}
327
}
328
329
buffer.toList
330
}
331
332
// Alternative using tap for logging
333
def loggedIteratorProcessing[T](iterator: Iterator[T]): List[T] = {
334
iterator
335
.tapEach(element => println(s"Processing: $element"))
336
.toList
337
}
338
```
339
340
### Comparing Collection Sizes Efficiently
341
342
```scala
343
def selectSmallestCollection[T](collections: List[Traversable[T]]): Option[Traversable[T]] = {
344
collections match {
345
case Nil => None
346
case head :: tail =>
347
Some(tail.foldLeft(head) { (smallest, current) =>
348
if (current.sizeCompare(smallest) < 0) current else smallest
349
})
350
}
351
}
352
353
val lists = List(
354
List(1, 2, 3),
355
List(4, 5, 6, 7, 8),
356
List(9, 10)
357
)
358
359
val smallest = selectSmallestCollection(lists) // Some(List(9, 10))
360
```
361
362
### Batch Processing with Size Constraints
363
364
```scala
365
def createBalancedBatches[T](
366
items: Traversable[T],
367
minBatchSize: Int,
368
maxBatchSize: Int
369
): List[List[T]] = {
370
371
if (items.sizeIs < minBatchSize) {
372
List(items.toList) // Single batch if too few items
373
} else {
374
val iterator = items.toIterator
375
val batches = scala.collection.mutable.ListBuffer[List[T]]()
376
377
while (iterator.hasNext) {
378
val batch = iterator.take(maxBatchSize).toList
379
if (batch.sizeIs >= minBatchSize || !iterator.hasNext) {
380
batches += batch
381
} else {
382
// Combine small final batch with previous batch
383
if (batches.nonEmpty) {
384
val lastBatch = batches.remove(batches.length - 1)
385
batches += (lastBatch ++ batch)
386
} else {
387
batches += batch
388
}
389
}
390
}
391
392
batches.toList
393
}
394
}
395
```
396
397
## Implementation Notes
398
399
- Size comparison operations use optimized implementations that can short-circuit
400
- For `Seq` types, `lengthCompare` is used when available for O(1) comparisons
401
- Iterator extensions preserve lazy evaluation where possible
402
- `tapEach` creates a new iterator that applies the side effect function during iteration
403
- All operations are implemented as value classes for minimal runtime overhead