From 3fd3a63428cee79f951acc66eb9e6f3cab264ab7 Mon Sep 17 00:00:00 2001 From: Oleksii Dykov Date: Fri, 7 May 2021 23:03:34 +0300 Subject: [PATCH] Optimize '.after' method --- .../dgroup/enumerable4j/Enumerable.java | 31 ++++--- .../github/dgroup/enumerable4j/AfterTest.java | 80 ++++++++++++++----- 2 files changed, 79 insertions(+), 32 deletions(-) diff --git a/src/main/java/io/github/dgroup/enumerable4j/Enumerable.java b/src/main/java/io/github/dgroup/enumerable4j/Enumerable.java index a45d6dc..6ea7874 100644 --- a/src/main/java/io/github/dgroup/enumerable4j/Enumerable.java +++ b/src/main/java/io/github/dgroup/enumerable4j/Enumerable.java @@ -220,32 +220,41 @@ default X reduce(X idn, BinaryOperator opr) { * @return The enumerable. */ default Enumerable after(Predicate prd) { - return this.after(prd, this.size() - 1); + return this.after(prd, this.size()); } /** * Returns an enumerable containing a certain number of elements of enumerable * after the first one which corresponds the condition. - * If no predicate (null) is given, then 'this' is returned instead. + * If no predicate (null) is given, then 'this' of the specified size is returned instead. * @param prd The function to match element after which enumerable elements should be returned. * @param size The number of elements the enumerable should be limited to. * @return The enumerable. */ default Enumerable after(Predicate prd, long size) { final Enumerable out; - if (prd == null) { - out = this; + if (size < 0) { + throw new IllegalArgumentException(Long.toString(size)); + } else if (size == 0) { + out = new Empty<>(); + } else if (prd == null) { + out = new Linked<>(this.stream().limit(size).collect(Collectors.toList())); } else { - int skip = 0; + boolean found = false; + out = new Linked<>(); + long cnt = size; for (final X elem : this) { - ++skip; - if (prd.test(elem)) { - break; + if (!found && prd.negate().test(elem)) { + continue; + } else if (!found) { + found = true; + continue; + } + if (cnt > 0) { + --cnt; + out.add(elem); } } - out = new Linked<>( - this.stream().skip(skip).limit(size).collect(Collectors.toList()) - ); } return out; } diff --git a/src/test/java/io/github/dgroup/enumerable4j/AfterTest.java b/src/test/java/io/github/dgroup/enumerable4j/AfterTest.java index ae8ee53..a9c6e71 100644 --- a/src/test/java/io/github/dgroup/enumerable4j/AfterTest.java +++ b/src/test/java/io/github/dgroup/enumerable4j/AfterTest.java @@ -24,6 +24,8 @@ package io.github.dgroup.enumerable4j; +import org.hamcrest.collection.IsEmptyIterable; +import org.hamcrest.core.AllOf; import org.junit.jupiter.api.Test; import org.llorllale.cactoos.matchers.Assertion; import org.llorllale.cactoos.matchers.HasSize; @@ -44,34 +46,34 @@ void nullPredicate() { new Assertion<>( "In case null-function the self enumerable is expected", new Linked<>(1, 2, 3, 4).after(null), - new HasValues<>(1, 2, 3, 4) + new AllOf<>( + new HasSize(4), + new HasValues<>(1, 2, 3, 4) + ) ).affirm(); } @Test void allAfter() { new Assertion<>( - "All elements after the first one which corresponds the condition", - new Linked<>(1, 2, 3, 4, 5, 6, 7).after(n -> n > 2), - new HasValues<>(4, 5, 6, 7) + "Returns elements after the first one which corresponds the condition", + new Linked<>(1, 2, 3, 4, 5).after(n -> n > 1), + new AllOf<>( + new HasSize(3), + new HasValues<>(3, 4, 5) + ) ).affirm(); } @Test - void firstThreeAfter() { + void firstTwoAfter() { new Assertion<>( - "First 3 elements after the first one which corresponds the condition", - new Linked<>(1, 2, 3, 4, 5, 6, 7).after(n -> n > 2, 3), - new HasValues<>(4, 5, 6) - ).affirm(); - } - - @Test - void allStringsAfter() { - new Assertion<>( - "All string elements after the first one which corresponds the condition", - new Linked<>("a", "b", "c", "d", "e").after(s -> s.equals("c")), - new HasValues<>("d", "e") + "Returns first 2 elements after the first one which corresponds the condition", + new Linked<>("a", "b", "c", "d", "e").after(s -> s.equals("b"), 2), + new AllOf<>( + new HasSize(2), + new HasValues<>("c", "d") + ) ).affirm(); } @@ -79,8 +81,11 @@ void allStringsAfter() { void firstTenAfter() { new Assertion<>( "The specified size can be greater than an actual enumerable size", - new Linked<>(1, 2, 3, 4, 5).after(n -> n == 3, 10), - new HasValues<>(4, 5) + new Linked<>(1, 2, 3, 4, 5).after(n -> n == 2, 10), + new AllOf<>( + new HasSize(3), + new HasValues<>(3, 4, 5) + ) ).affirm(); } @@ -89,16 +94,49 @@ void allAfterFirst() { new Assertion<>( "The first element of enumerable corresponds the condition", new Linked<>(1, 2, 3, 4, 5).after(n -> n < 10), + new AllOf<>( + new HasSize(4), new HasValues<>(2, 3, 4, 5) + ) ).affirm(); } @Test void noneMatch() { new Assertion<>( - "No one elements corresponds the condition, an empty collection is returned", + "No one element corresponds the condition, an empty collection is returned", new Linked<>(1, 2, 3).after(n -> n < 0), - new HasSize(0) + new IsEmptyIterable<>() + ).affirm(); + } + + @Test + void zeroSize() { + new Assertion<>( + "If the size value is 0, an empty collection is returned", + new Linked<>(1, 2, 3).after(n -> n < 10, 0), + new IsEmptyIterable<>() + ).affirm(); + } + + @Test + void nullPredicateWithSize() { + new Assertion<>( + "In case null-function and specified size a enumerable of the size is expected", + new Linked<>(1, 2, 3, 4).after(null, 2), + new AllOf<>( + new HasSize(2), + new HasValues<>(1, 2) + ) + ).affirm(); + } + + @Test + void nullPredicateAndZeroSize() { + new Assertion<>( + "If the size value is 0, an empty collection is returned", + new Linked<>(1, 2, 3).after(null, 0), + new IsEmptyIterable<>() ).affirm(); }