Reputation: 1907
This question have been asked in a context of forEach
.
Comment(after the answer was accepted): I accepted the answer of @nullpointer, but it is the right one only in the context of my code example, not in the general question about break-ability of reduce..
The question:
But is there a way in reduce
or in collect
to "break" prematurely, without going through all the stream elements? (That means I need accumulate the state while iterating, so I use reduce
or collect
).
In short: I need iterate all elements of the stream (elements are integers and ordered from small to big), but look into 2 neighbor elements and compare them, if difference between them is greater than 1, I need "break" and stop "accumulate the state" and I need return the last passed element.
Variant to throw a RuntimeException
and variant to pass external state - bad for me.
Code example with comments :
public class Solution {
public int solution(int[] A) {
Supplier<int[]> supplier = new Supplier<int[]>() {
@Override
public int[] get() {
//the array describes the accumulated state:
//first element in the array , if set > 0, means - the result is achieved, we can stop iterate over the rest elements
//second element in the array will represent the "previous element" while iterating the stream
return new int[]{0, 0};
}
};
//the array in accumulator describes the accumulated state:
//first element in the array , if set > 0, means - the result is achieved, we can stop iterate over the rest elements
//second element in the array will represent the "previous element" while iterating the stream
ObjIntConsumer<int[]> accumulator = new ObjIntConsumer<int[]>() {
@Override
public void accept(int[] sett, int value) {
if (sett[0] > 0) {
;//do nothing, result is set
} else {
if (sett[1] > 0) {//previous element exists
if (sett[1] + 1 < value) {
sett[0] = sett[1] + 1;
} else {
sett[1] = value;
}
} else {
sett[1] = value;
}
}
}
};
BiConsumer<int[], int[]> combiner = new BiConsumer<int[], int[]>() {
@Override
public void accept(int[] sett1, int[] sett2) {
System.out.println("Combiner is not used, we are in sequence");
}
};
int result[] = Arrays.stream(A).sorted().filter(value -> value > 0).collect(supplier, accumulator, combiner);
return result[0];
}
/**
* We have an input array
* We need order it, filter out all elements that <=0 (to have only positive)
* We need find a first minimal integer that does not exist in the array
* In this example it is 5
* Because 4,6,16,32,67 positive integers array is having 5 like a minimum that not in the array (between 4 and 6)
*
* @param args
*/
public static void main(String[] args) {
int[] a = new int[]{-2, 4, 6, 16, -7, 0, 0, 0, 32, 67};
Solution s = new Solution();
System.out.println("The value is " + s.solution(a));
}
}
Upvotes: 5
Views: 4626
Reputation: 32028
Given an array as input, seems to me you're looking for something like this :
int stateStream(int[] arr) {
return IntStream.range(0, arr.length - 1)
.filter(i -> arr[i + 1] - arr[i] > 1) // your condition
.mapToObj(i -> arr[i])
.findFirst() // first such occurrence
.map(i -> i + 1) // to add 1 to the point where the cehck actually failed
.orElse(0); // some default value
}
or from the scratch, while you convert it to a sorted and filtered value list as :
int stateStream(int[] arr) {
List<Integer> list = Arrays.stream(arr)
.boxed().sorted()
.filter(value -> value > 0)
.collect(Collectors.toList());
return IntStream.range(0, list.size() - 1)
.filter(i -> list.get(i + 1) - list.get(i) > 1)
.mapToObj(list::get)
.findFirst()
.map(i -> i + 1)
.orElse(0);
}
Upvotes: 3
Reputation: 1863
The is no ways in streams API to break
. You can throw an Exception, but it's really not a good idea.
But you are right - you can use reduce to find last "successed" element in collection
The list of ints:
List<Integer> integers = Arrays.asList(1,2,3,4,5,6,7,8,9,10,12,13);
Lets find the value of i-th element, where element[i+1]-element[i] > 1
:
int result = integers.stream().reduce((i1,i2) -> (i2-i1) > 1 ? i1 : i2).get();
For this case result will be equals to 10. And then you can just get the sublist of your common list;
integers.subList(0,integers.indexOf(result)+1).forEach(s-> System.out.println(s));
For cases of valid collection (when there are no elements with difference > 1) the result
will be equals to the value of last element and sublist will be equals to list. So you can add some check to avoid .subList
when it's not neccessary.
Example for reduce:
{1,2,3,5}
Step1:
i1 = 1; i2 = 2; -> reduce(), difference =1, so we reduce this pair to i2 (2) -> new collection is{2,3,5}
Step2
i1 = 2; i2 = 3; -> reduce(), difference =1, so we reduce this pair to i2 (3) -> new collection is{3,5}
Step3
i1 = 3; i2 = 5; -> reduce(), difference >1, so we reduce this pair to i1 (3) -> new collection is {3} and it transforms to Optional<Integer>
Upvotes: 2