I want to ensure that all numbers in the list are grouped together. Let me explain this on examples:
{1, 1, 1, 2, 2} // OK, two distinct groups
{1, 1, 2, 2, 1, 1} // Bad, two groups with "1"
{1, 2, 3, 4} // OK, 4 distinct groups of size 1
{1, 1, 1, 1} // OK, 1 group
{3, 4, 3} // Bad, two groups with "3"
{99, -99, 99} // Bad, two groups with "99"
{} // OK, no groups
Here's how I obtain the stream:
IntStream.of(numbers)
...
Now I need to pass or return true for "OK" examples and throw AssertionError
or return false on "Bad" examples. How can I do that using Stream API?
Here's my current solution with additional Set
created:
Set<Integer> previousNumbers = new HashSet<>();
IntStream.of(numbers)
.reduce(null, (previousNumber, currentNumber) -> {
if (currentNumber == previousNumber) {
assertThat(previousNumbers).doesNotContain(currentNumber);
previousNumbers.add(currentNumber);
}
return currentNumber;
}
);
Using my free StreamEx library:
IntStreamEx.of(numbers).boxed().runLengths().toMap();
This code will throw IllegalStateException
if there are repeating groups.
Here runLengths()
method is used. It collapses equal adjacent elements replacing them with Map.Entry
where key is the input element and value is the number of repeats. Finally toMap()
is used which is a shortcut for .collect(Collectors.toMap(Entry::getKey, Entry::getValue))
. We are using the fact that .toMap()
throws IllegalStateException
when keys repeat (unless custom mergeFunction is supplied).
As a free bonus on successful execution you will have a map where keys are input elements and values are lengths of series.
In my opinion this problem doesn't fit the Stream API
at all but I was curious how this could be implemenented (however in a performant way).
The problem is that you have to keep track of seen elements and the whole test should have a short-circuit behaviour. So I came up with this solution (without Streams
):
public static boolean hasUniqueGroups(int[] arr) {
Objects.requireNonNull(arr);
Set<Integer> seen = new HashSet<>();
for (int i = 0; i < arr.length; i++) {
if (i == 0 || arr[i] != arr[i - 1]) {
if (!seen.add(arr[i])) {
return false;
}
}
}
return true;
}
The next step is to introduce the Stream API
and the solution looks like this:
public static boolean hasUniqueGroups(int[] arr) {
Objects.requireNonNull(arr);
Set<Integer> seen = new HashSet<>();
return IntStream.range(0, arr.length)
.filter(i -> i == 0 || arr[i] != arr[i - 1])
.mapToObj(i -> arr[i])
.allMatch(seen::add);
}
Note: In order to parallelize this Stream
you should use a thread-safe Set
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With