Skip to content

Commit 4219e0f

Browse files
committed
- fixes #8
1 parent 81415a7 commit 4219e0f

File tree

3 files changed

+246
-16
lines changed

3 files changed

+246
-16
lines changed

src/main/java/br/com/zbra/androidlinq/AbstractStream.java

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,14 +150,55 @@ public T first(Predicate<T> predicate) {
150150
return where(predicate).first();
151151
}
152152

153+
@Override
154+
public T firstOrNull() {
155+
return firstOrDefault(null);
156+
}
157+
158+
@Override
159+
public T firstOrNull(Predicate<T> predicate) {
160+
return firstOrDefault(predicate, null);
161+
}
162+
163+
@Override
164+
public T firstOrDefault(T defaultValue) {
165+
Iterator<T> iterator = iterator();
166+
return iterator.hasNext() ? iterator.next() : defaultValue;
167+
}
168+
169+
@Override
170+
public T firstOrDefault(Predicate<T> predicate, T defaultValue) {
171+
return where(predicate).firstOrDefault(defaultValue);
172+
}
173+
153174
@Override
154175
public T last() {
155-
return reverseIterator().next();
176+
return reverse().first();
156177
}
157178

158179
@Override
159180
public T last(Predicate<T> predicate) {
160-
return where(predicate).last();
181+
return reverse().first(predicate);
182+
}
183+
184+
@Override
185+
public T lastOrNull() {
186+
return lastOrDefault(null);
187+
}
188+
189+
@Override
190+
public T lastOrNull(Predicate<T> predicate) {
191+
return lastOrDefault(predicate, null);
192+
}
193+
194+
@Override
195+
public T lastOrDefault(T defaultValue) {
196+
return reverse().firstOrDefault(defaultValue);
197+
}
198+
199+
@Override
200+
public T lastOrDefault(Predicate<T> predicate, T defaultValue) {
201+
return reverse().firstOrDefault(predicate, defaultValue);
161202
}
162203

163204
@Override
@@ -174,6 +215,32 @@ public T single(Predicate<T> predicate) throws MultipleElementsFoundException {
174215
return where(predicate).single();
175216
}
176217

218+
@Override
219+
public T singleOrNull() {
220+
return singleOrDefault(null);
221+
}
222+
223+
@Override
224+
public T singleOrNull(Predicate<T> predicate) {
225+
return singleOrDefault(predicate, null);
226+
}
227+
228+
@Override
229+
public T singleOrDefault(T defaultValue) {
230+
Iterator<T> iterator = iterator();
231+
if (!iterator.hasNext())
232+
return defaultValue;
233+
T result = iterator.next();
234+
if (iterator.hasNext())
235+
throw new MultipleElementsFoundException();
236+
return result;
237+
}
238+
239+
@Override
240+
public T singleOrDefault(Predicate<T> predicate, T defaultValue) {
241+
return where(predicate).singleOrDefault(defaultValue);
242+
}
243+
177244
@Override
178245
public List<T> toList() {
179246
List<T> list = new ArrayList<>();

src/main/java/br/com/zbra/androidlinq/Stream.java

Lines changed: 114 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.math.BigDecimal;
77
import java.util.List;
88
import java.util.Map;
9+
import java.util.NoSuchElementException;
910

1011
/**
1112
* Decorates {@code Iterable<T>} objects to enable use of Linq like expressions.
@@ -225,48 +226,131 @@ public interface Stream<T> extends Iterable<T> {
225226
public int count();
226227

227228
/**
228-
* Returns the first element of a sequence.
229+
* Returns the first element of a sequence; this method throws an exception if the sequence is empty.
229230
*
230231
* @return The first element in the specified sequence.
231-
* @throws java.util.NoSuchElementException if the sequence is empty.
232+
* @throws NoSuchElementException if the sequence is empty.
232233
*/
233234
public T first();
234235

235236
/**
236-
* Returns the first element in a sequence that satisfies a specified condition.
237+
* Returns the first element in a sequence that satisfies the specified condition; this method throws
238+
* an exception if no element in the sequence satisfies the condition.
237239
*
238240
* @param predicate A function to test each element for a condition.
239241
* @return The first element in the sequence that passes the test in the specified predicate function.
240-
* @throws java.util.NoSuchElementException if no element matches the sequence.
242+
* @throws NoSuchElementException if no element in the sequence satisfies the condition.
241243
*/
242244
public T first(Predicate<T> predicate);
243245

244246
/**
245-
* Returns the last element of a sequence.
247+
* Returns the first element of a sequence, or null if empty.
248+
*
249+
* @return The first element in the specified sequence, or null if empty.
250+
*/
251+
public T firstOrNull();
252+
253+
/**
254+
* Returns the first element in a sequence that satisfies a specified condition, or null if none does.
255+
*
256+
* @param predicate A function to test each element for a condition.
257+
* @return The first element in the sequence that passes the test in the specified predicate function, or null if none does.
258+
*/
259+
public T firstOrNull(Predicate<T> predicate);
260+
261+
/**
262+
* Returns the first element of a sequence, or defaultValue if empty.
263+
*
264+
* @return The first element in the specified sequence, or defaultValue if empty.
265+
*/
266+
public T firstOrDefault(T defaultValue);
267+
268+
/**
269+
* Returns the first element in a sequence that satisfies a specified condition, or defaultValue if none does.
270+
*
271+
* @param predicate A function to test each element for a condition.
272+
* @return The first element in the sequence that passes the test in the specified predicate function, or or defaultValue if none does.
273+
*/
274+
public T firstOrDefault(Predicate<T> predicate, T defaultValue);
275+
276+
/**
277+
* Returns the last element of a sequence; this method throws an exception if the sequence is empty.
246278
*
247279
* @return The last element in the specified sequence.
248-
* @throws java.util.NoSuchElementException if the sequence is empty.
280+
* @throws NoSuchElementException if the sequence is empty.
249281
*/
250282
public T last();
251283

252284
/**
253-
* Returns the last element in a sequence that satisfies a specified condition.
285+
* Returns the last element in a sequence that satisfies the specified condition; this method throws
286+
* an exception if no element in the sequence satisfies the condition.
254287
*
255288
* @param predicate A function to test each element for a condition.
256289
* @return The last element in the sequence that passes the test in the specified predicate function.
257-
* @throws java.util.NoSuchElementException if no element matches the sequence.
290+
* @throws NoSuchElementException if no element in the sequence satisfies the condition.
258291
*/
259292
public T last(Predicate<T> predicate);
260293

261294
/**
262-
* Returns the only element of a sequence, or a default value if the sequence is empty; this method throws
263-
* an exception if there is more than one element in the sequence.
295+
* Returns the last element of a sequence, or null if empty.
296+
*
297+
* @return The last element in the specified sequence, or null if empty.
298+
*/
299+
public T lastOrNull();
300+
301+
/**
302+
* Returns the last element in a sequence that satisfies a specified condition, or null if none does.
303+
*
304+
* @param predicate A function to test each element for a condition.
305+
* @return The last element in the sequence that passes the test in the specified predicate function, or null if none does.
306+
*/
307+
public T lastOrNull(Predicate<T> predicate);
308+
309+
/**
310+
* Returns the last element of a sequence, or defaultValue if empty.
311+
*
312+
* @return The last element in the specified sequence, or defaultValue if empty.
313+
*/
314+
public T lastOrDefault(T defaultValue);
315+
316+
/**
317+
* Returns the last element in a sequence that satisfies a specified condition, or defaultValue if none does.
318+
*
319+
* @param predicate A function to test each element for a condition.
320+
* @return The last element in the sequence that passes the test in the specified predicate function, or or defaultValue if none does.
321+
*/
322+
public T lastOrDefault(Predicate<T> predicate, T defaultValue);
323+
324+
/**
325+
* Returns the only element of a sequence; this method throws an exception if there is more than
326+
* one element in the sequence or if the sequence is empty.
264327
*
265328
* @return The single element of the input sequence, or default(TSource) if the sequence contains no elements.
266329
* @throws MultipleElementsFoundException The input sequence contains more than one matching element.
330+
* @throws NoSuchElementException If the sequence is empty.
267331
*/
268332
public T single() throws MultipleElementsFoundException;
269333

334+
/**
335+
* Returns the only element of a sequence that satisfies a specified condition; this method throws an exception
336+
* if multiple elements in the sequence satisfy the condition or if none does.
337+
*
338+
* @param predicate A function to test an element for a condition.
339+
* @return The single element of the input sequence that satisfies the condition, or null if no such element is found.
340+
* @throws MultipleElementsFoundException The input sequence contains more than one matching element.
341+
* @throws NoSuchElementException If no element in the sequence satisfies the condition.
342+
*/
343+
public T single(Predicate<T> predicate) throws MultipleElementsFoundException;
344+
345+
/**
346+
* Returns the only element of a sequence, or a null if the sequence is empty; this method throws
347+
* an exception if there is more than one element in the sequence.
348+
*
349+
* @return The single element of the input sequence, or default(TSource) if the sequence contains no elements.
350+
* @throws MultipleElementsFoundException The input sequence contains more than one matching element.
351+
*/
352+
public T singleOrNull();
353+
270354
/**
271355
* Returns the only element of a sequence that satisfies a specified condition or null if
272356
* no such element exists; this method throws an exception if more than one element satisfies the condition.
@@ -275,7 +359,26 @@ public interface Stream<T> extends Iterable<T> {
275359
* @return The single element of the input sequence that satisfies the condition, or null if no such element is found.
276360
* @throws MultipleElementsFoundException The input sequence contains more than one matching element.
277361
*/
278-
public T single(Predicate<T> predicate) throws MultipleElementsFoundException;
362+
public T singleOrNull(Predicate<T> predicate);
363+
364+
/**
365+
* Returns the only element of a sequence, or a defaultValue if the sequence is empty; this method throws
366+
* an exception if there is more than one element in the sequence.
367+
*
368+
* @return The single element of the input sequence, or default(TSource) if the sequence contains no elements.
369+
* @throws MultipleElementsFoundException The input sequence contains more than one matching element.
370+
*/
371+
public T singleOrDefault(T defaultValue);
372+
373+
/**
374+
* Returns the only element of a sequence that satisfies a specified condition or defaultValue if
375+
* no such element exists; this method throws an exception if more than one element satisfies the condition.
376+
*
377+
* @param predicate A function to test an element for a condition.
378+
* @return The single element of the input sequence that satisfies the condition, or null if no such element is found.
379+
* @throws MultipleElementsFoundException The input sequence contains more than one matching element.
380+
*/
381+
public T singleOrDefault(Predicate<T> predicate, T defaultValue);
279382

280383
/**
281384
* Creates a List from a Stream.

src/test/groovy/br/com/zbra/androidlinq/StreamTest.groovy

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,29 +249,61 @@ class StreamTest extends GroovyTestCase {
249249
shouldFail(NoSuchElementException.class, {
250250
assert stream(integers).first({ n -> n > 10 })
251251
})
252+
253+
// firstOrDefault
254+
assert stream([]).firstOrDefault(10) == 10
255+
assert stream(integers).firstOrDefault(10) == 0
256+
257+
// firstOrDefault passing Predicate
258+
assert stream(integers).firstOrDefault({ n -> n > 5 }, 10) == 6
259+
assert stream(integers).firstOrDefault({ n -> n == 100 }, -1) == -1
260+
261+
// firstOrNull
262+
assert stream([]).firstOrNull() == null
263+
assert stream(integers).firstOrNull() == 0
264+
265+
// firstOrDefault passing Predicate
266+
assert stream(integers).firstOrNull({ n -> n > 5 }) == 6
267+
assert stream(integers).firstOrNull({ n -> n == 100 }) == null
252268
}
253269

254270
void testLast() {
255271
def integers = 0..9
256272

257-
// first with no parameters
273+
// last with no parameters
258274
assert stream(integers).last() == 9
259275
shouldFail(NoSuchElementException.class, {
260276
assert stream([]).last()
261277
})
262278

263-
// first passing Predicate
279+
// last passing Predicate
264280
assert stream(integers).last({ n -> n < 5 }) == 4
265281
shouldFail(NoSuchElementException.class, {
266282
assert stream(integers).last({ n -> n > 10 })
267283
})
284+
285+
// lastOrDefault
286+
assert stream([]).lastOrDefault(10) == 10
287+
assert stream(integers).lastOrDefault(10) == 9
288+
289+
// lastOrDefault passing Predicate
290+
assert stream(integers).lastOrDefault({ n -> n < 5 }, 10) == 4
291+
assert stream(integers).lastOrDefault({ n -> n == 100 }, -1) == -1
292+
293+
// lastOrNull
294+
assert stream([]).lastOrNull() == null
295+
assert stream(integers).lastOrNull() == 9
296+
297+
// lastOrDefault passing Predicate
298+
assert stream(integers).lastOrNull({ n -> n < 5 }) == 4
299+
assert stream(integers).lastOrNull({ n -> n == 100 }) == null
268300
}
269301

270302
void testSingle() {
271303
def integers = 0..9
272304

273305
// single with no parameters
274-
assert stream([5]).single() == 5
306+
assert stream([5]).single() == 5
275307
shouldFail(NoSuchElementException.class, {
276308
stream([]).single()
277309
})
@@ -287,6 +319,34 @@ class StreamTest extends GroovyTestCase {
287319
shouldFail(MultipleElementsFoundException.class, {
288320
stream(integers).single({ n -> n > 5 })
289321
})
322+
323+
// singleOrDefault with no parameters
324+
assert stream([5]).singleOrDefault(10) == 5
325+
assert stream([]).singleOrDefault(10) == 10
326+
shouldFail(MultipleElementsFoundException.class, {
327+
stream(integers).singleOrDefault(10)
328+
})
329+
330+
// singleOrDefault passing Predicate
331+
assert stream(integers).singleOrDefault({ n -> n == 5 }, -1) == 5
332+
assert stream(integers).singleOrDefault({ n -> n == 10 }, -1) == -1
333+
shouldFail(MultipleElementsFoundException.class, {
334+
stream(integers).singleOrDefault({ n -> n > 5 }, -1)
335+
})
336+
337+
// singleOrNull with no parameters
338+
assert stream([5]).singleOrNull() == 5
339+
assert stream([]).singleOrNull() == null
340+
shouldFail(MultipleElementsFoundException.class, {
341+
stream(integers).singleOrNull()
342+
})
343+
344+
// singleOrNull passing Predicate
345+
assert stream(integers).singleOrNull({ n -> n == 5 }) == 5
346+
assert stream(integers).singleOrNull({ n -> n == 10 }) == null
347+
shouldFail(MultipleElementsFoundException.class, {
348+
stream(integers).singleOrNull({ n -> n > 5 })
349+
})
290350
}
291351

292352
void testToList() {

0 commit comments

Comments
 (0)