Skip to content

Commit

Permalink
[#32] merging() renamed to dominators(); javaDoc, test, changes,
Browse files Browse the repository at this point in the history
cheatsheet updates; link from StreamEx.collapse() to
MoreCollectors.dominators()
  • Loading branch information
amaembo committed Dec 15, 2015
1 parent 4783c67 commit 8c7c706
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 36 deletions.
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* [#13] Added: `StreamEx.split` to split with single character delimiter.
* [#35] Added: construction of all stream types from the `Iterator`.
* [#36] Added: `StreamEx.of(Enumeration)` static method.
* [#32] Added: `MoreCollectors.merging` collector which collects elements to the list removing the adjacent elements by `BiPredicate`.
* [#32] Added: `MoreCollectors.dominators` collector which collects the elements to the list leaving only "dominators".
* Updated documentation.

### 0.5.0
Expand Down
2 changes: 1 addition & 1 deletion CHEATSHEET.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ Join the elements into string with possible limit to the string length (adding e
Perform a group-by with the specified keys domain, so every key is initialized even if absent in the input | `MoreCollectors.groupingBy()/groupingByEnum()`
Partition input according to the `Predicate` | `MoreCollectors.partitioningBy()`
Get the common prefix or common suffix `String` of input elements | `MoreCollectors.commonPrefix()/commonSuffix()`
Get the list of input elements removing the children elements which follow their parent | `MoreCollectors.merging()`
Get the list of input elements removing the elements which follow their dominator element | `MoreCollectors.dominators()`

### Adaptor collectors

Expand Down
43 changes: 24 additions & 19 deletions src/main/java/one/util/streamex/MoreCollectors.java
Original file line number Diff line number Diff line change
Expand Up @@ -1522,47 +1522,52 @@ public static <T, A, R, RR> Collector<T, A, RR> collectingAndThen(Collector<T, A

/**
* Returns a collector which collects input elements into {@code List}
* replacing the series of adjacent elements which satisfy given
* {@code BiPredicate} with the leftmost element.
* removing the elements following their dominator element. The dominator
* elements are defined according to given isDominator {@code BiPredicate}.
*
* <p>
* This operation is similar to
* {@code streamEx.collapse(mergeable).toList()}. The important difference
* {@code streamEx.collapse(isDominator).toList()}. The important difference
* is that in this method {@code BiPredicate} accepts not the adjacent
* stream elements, but the leftmost element of the series and the current
* element. This collector is useful to remove elements which are considered
* to be the part of some parent element assuming the input is pre-sorted so
* all children elements immediately follow their parent.
* stream elements, but the leftmost element of the series (current
* dominator) and the current element.
*
* <p>
* Note that for correct parallel processing the {@code BiPredicate} must
* respect the following rules:
* For example, consider the stream of numbers:
*
* <pre>{@code
* mergeable(A, B) && mergeable(B, C) => mergeable(A, C); // transitivity
* mergeable(A, B) && mergeable(A, C) => !mergeable(B, C);
* StreamEx<Integer> stream = StreamEx.of(1, 5, 3, 4, 2, 7);
* }</pre>
*
* <p>
* Using {@code stream.collapse((a, b) -> a >= b).toList()} you will get the
* numbers which are bigger than their immediate predecessor (
* {@code [1, 5, 4, 7]}). However using
* {@code stream.collect(dominators((a, b) -> a >= b))} you will get the
* numbers which are bigger than any predecessor ({@code [1, 5, 7]}).
*
* @param <T>
* type of the input elements.
* @param mergeable
* a {@code BiPredicate} which takes the leftmost element of the
* series and the current element and returns true if the current
* element belongs to the same series.
* @param isDominator
* a non-interfering, stateless, transitive {@code BiPredicate}
* which takes the leftmost element of the series and the current
* element and returns true if the current element belongs to the
* same series.
* @return a collector which collects input element into {@code List}
* leaving only leftmost element of every series.
* leaving only dominator elements.
* @see StreamEx#collapse(BiPredicate)
* @since 0.5.1
*/
public static <T> Collector<T, ?, List<T>> merging(BiPredicate<? super T, ? super T> mergeable) {
public static <T> Collector<T, ?, List<T>> dominators(BiPredicate<? super T, ? super T> isDominator) {
return Collector.of(ArrayList::new, (acc, t) -> {
if (acc.isEmpty() || !mergeable.test(acc.get(acc.size() - 1), t))
if (acc.isEmpty() || !isDominator.test(acc.get(acc.size() - 1), t))
acc.add(t);
}, (acc1, acc2) -> {
if (acc1.isEmpty())
return acc2;
int i = 0, l = acc2.size();
T last = acc1.get(acc1.size() - 1);
while (i < l && mergeable.test(last, acc2.get(i)))
while (i < l && isDominator.test(last, acc2.get(i)))
i++;
if (i < l)
acc1.addAll(acc2.subList(i, l));
Expand Down
19 changes: 13 additions & 6 deletions src/main/java/one/util/streamex/StreamEx.java
Original file line number Diff line number Diff line change
Expand Up @@ -1231,7 +1231,7 @@ public void forPairs(BiConsumer<? super T, ? super T> action) {
*
* <p>
* This is a <a href="package-summary.html#StreamOps">quasi-intermediate</a>
* operation.
* partial reduction operation.
*
* <p>
* This operation is equivalent to
Expand Down Expand Up @@ -1260,7 +1260,7 @@ public StreamEx<T> collapse(BiPredicate<? super T, ? super T> collapsible, Binar
*
* <p>
* This is a <a href="package-summary.html#StreamOps">quasi-intermediate</a>
* operation.
* partial reduction operation.
*
* @param <R>
* the type of the elements in the resulting stream
Expand Down Expand Up @@ -1303,14 +1303,20 @@ public <R, A> StreamEx<R> collapse(BiPredicate<? super T, ? super T> collapsible
*
* <p>
* This is a <a href="package-summary.html#StreamOps">quasi-intermediate</a>
* operation.
* partial reduction operation.
*
* <p>
* This operation is equivalent to
* {@code collapse(collapsible, MoreCollectors.first()).map(Optional::get)}
* , but more efficient.
*
* <p>
* Note that this operation always tests the adjacent pairs of input
* elements. In some scenarios it's desired to test every element with the
* first element of the current series. In this case consider using
* {@link MoreCollectors#dominators(BiPredicate)} collector instead.
*
* <p>
* For sorted stream {@code collapse(Objects::equals)} is equivalent to
* {@code distinct()}.
*
Expand All @@ -1319,6 +1325,7 @@ public <R, A> StreamEx<R> collapse(BiPredicate<? super T, ? super T> collapsible
* adjacent input elements which returns true for elements which
* are collapsible.
* @return the new stream
* @see MoreCollectors#dominators(BiPredicate)
* @since 0.3.1
*/
public StreamEx<T> collapse(BiPredicate<? super T, ? super T> collapsible) {
Expand All @@ -1332,7 +1339,7 @@ public StreamEx<T> collapse(BiPredicate<? super T, ? super T> collapsible) {
*
* <p>
* This is a <a href="package-summary.html#StreamOps">quasi-intermediate</a>
* operation.
* partial reduction operation.
*
* <p>
* For sorted input {@code runLengths().toMap()} is the same as
Expand All @@ -1359,7 +1366,7 @@ public EntryStream<T, Long> runLengths() {
*
* <p>
* This is a <a href="package-summary.html#StreamOps">quasi-intermediate</a>
* operation.
* partial reduction operation.
*
* <p>
* There are no guarantees on the type, mutability, serializability, or
Expand Down Expand Up @@ -1402,7 +1409,7 @@ public StreamEx<List<T>> groupRuns(BiPredicate<? super T, ? super T> sameGroup)
*
* <p>
* This is a <a href="package-summary.html#StreamOps">quasi-intermediate</a>
* operation. This operation is the same as
* partial reduction operation. This operation is the same as
* {@code groupRuns(sameInterval).map(list -> mapper.apply(list.get(0), list.get(list.size()-1)))}
* , but has less overhead as only first and last elements of each interval
* are tracked.
Expand Down
42 changes: 33 additions & 9 deletions src/test/java/one/util/streamex/MoreCollectorsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -630,15 +630,19 @@ public void testCommonSuffix() {
}

@Test
public void testMerging() {
List<String> input = Arrays.asList("a/", "a/b/c/", "b/c/", "b/d/", "c/a/", "d/a/b/", "c/a/b/", "c/b/", "b/c/d/");
public void testDominators() {
List<String> input = Arrays
.asList("a/", "a/b/c/", "b/c/", "b/d/", "c/a/", "d/a/b/", "c/a/b/", "c/b/", "b/c/d/");
List<String> expected = Arrays.asList("a/", "b/c/", "b/d/", "c/a/", "c/b/", "d/a/b/");
checkCollector("merging", expected, () -> input.stream().sorted(), MoreCollectors.merging((a, b) -> b.startsWith(a)));

checkCollector("dominators", expected, () -> input.stream().sorted(),
MoreCollectors.dominators((a, b) -> b.startsWith(a)));

Random r = new Random(1);

List<String> longInput = StreamEx.generate(() -> IntStreamEx.of(r, r.nextInt(10)+3, 'a', 'z').mapToObj(ch -> (char)ch).joining("/", "", "/"))
.limit(1000).toList();

List<String> longInput = StreamEx
.generate(
() -> IntStreamEx.of(r, r.nextInt(10) + 3, 'a', 'z').mapToObj(ch -> (char) ch)
.joining("/", "", "/")).limit(1000).toList();

List<String> tmp = StreamEx.of(longInput).sorted().toList();
List<String> result = new ArrayList<>();
Expand All @@ -652,8 +656,28 @@ public void testMerging() {
if (last != null && curr.startsWith(last)) {
curr = last;
last = oldLast;
} else result.add(curr);
} else
result.add(curr);
}
checkCollector("dominatorsLong", result, () -> longInput.stream().sorted(),
MoreCollectors.dominators((a, b) -> b.startsWith(a)));
}

@Test
public void testIncreasingDominators() {
int[] input = { 1, 3, 4, 2, 1, 7, 5, 3, 4, 0, 4, 6, 7, 10, 4, 3, 2, 1 };
List<Integer> result = Arrays.asList(1, 3, 4, 7, 10);
checkCollector("increasing", result, () -> IntStreamEx.of(input).boxed(), MoreCollectors.dominators((a, b) -> a >= b));
int[] longInput = new Random(1).ints(10000, 0, 1000000).toArray();
List<Integer> longResult = new ArrayList<>();
int curMax = -1;
for(int val : longInput) {
if(val > curMax) {
curMax = val;
longResult.add(curMax);
}
}
checkCollector("merginglong", result, () -> longInput.stream().sorted(), MoreCollectors.merging((a, b) -> b.startsWith(a)));
checkCollector("increasingLong", longResult, () -> IntStreamEx.of(longInput).boxed(),
MoreCollectors.dominators((a, b) -> a >= b));
}
}

0 comments on commit 8c7c706

Please sign in to comment.