Reputation: 5136
I am trying to find the fastest way, and less complex way to do some union/exclude/intersection operations over Java SE 8 Streams.
I am doing this:
Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");
Stream<String> fruitStream2 = Stream.of("orange", "kiwi", "melon", "apple", "watermelon");
//Trying to create exclude operation
fruitStream.filter(
item -> !fruitStream2.anyMatch(item2 -> item2.equals(item)))
.forEach(System.out::println);
// Expected result: fruitStream - fruitStream2: ["banana","pear"]
I get the following Exception:
java.lang.IllegalStateException: stream has already been operated upon or closed
If I was able to do this operation, I could develope myself all the rest, union, intersection etc...
So, the 2 points are:
1) What am I doing wrong in this solution to get that Exception?
2) Is there a less complex way to perform operations between 2 streams?
Note
I want to use streams to learn about them. Dont want to transform them into arrays or lists
Upvotes: 4
Views: 183
Reputation: 50716
This is kind of silly and it won't give you great performance, but it does fulfill your requirement of not (explicitly) creating arrays or lists:
public static void main(String[] args) {
new Object() {
Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");
Stream<String> fruitStream2 = Stream.of("orange", "kiwi", "melon", "apple", "watermelon");
{
fruitStream2.forEach(f -> fruitStream = fruitStream.filter(Predicate.isEqual(f).negate()));
fruitStream.forEach(System.out::println);
}
};
}
Output:
banana
pear
EDIT A (slightly) more straightforward approach:
public static void main(String[] args) {
Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");
Stream.of("orange", "kiwi", "melon", "apple", "watermelon")
.map(Predicate::isEqual)
.reduce(Predicate::or)
.map(Predicate::negate)
.map(fruitStream::filter)
.orElse(fruitStream)
.forEach(System.out::println);
}
Upvotes: 0
Reputation: 2523
The first time item -> !fruitStream2.anyMatch(item2 -> item2.equals(item))
executes, it consumes fruitStream2
. fruitStream2
can not be used again to filter the second item in fruitStream1
.
Rather than using a second stream, why not create a Set
and use contains
?
Set<String> otherFruits = new HashSet<>(); // Add the fruits.
fruitStream.filter(f -> !otherFruits.contains(f)).forEach(System.out::println);
Upvotes: 6
Reputation: 109547
A Stream is an inside iteration/processing executed only once. That is a very limited area.
Supplier<Stream<String>> fruitStream2S = () ->
Stream.of("orange", "kiwi", "melon", "apple", "watermelon");
fruitStream.filter(item -> !fruitStream2s.get().anyMatch(item2 -> item2.equals(item)))
.forEach(System.out::println);
Which is not efficient.
Upvotes: 1
Reputation: 44965
What am I doing wrong in this solution to get that Exception?
You cannot consume several times the same Stream
and here you consume fruitStream2
as many times as you have elements in fruitStream
Is there a less complex way to perform operations between 2 streams?
You could convert the second Stream
as a Set
:
Stream<String> fruitStream = Stream.of("apple", "banana", "pear", "kiwi", "orange");
Set<String> fruitSet = Stream.of("orange", "kiwi", "melon", "apple", "watermelon")
.collect(Collectors.toSet());
fruitStream.filter(item -> !fruitSet.contains(item)).forEach(System.out::println);
Output:
banana
pear
Upvotes: 4