4

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

3
  • "Dont want to transform them into arrays or lists" You might not want to, but there's not going to be another solution that doesn't involve storing one stream in a collection. Commented Sep 1, 2016 at 18:26
  • You realize varargs implicitly creates an array for you, right? Commented Sep 1, 2016 at 23:53
  • If you want to ignore the fact that Stream.of does the array creation behind the scenes, you can simply use fruitStream.filter( item -> !Stream.of("orange", "kiwi", "melon", "apple", "watermelon").anyMatch(item2 -> item2.equals(item)))…, but of course, a HashSet based solution is more efficient. Commented Sep 2, 2016 at 8:31

4 Answers 4

7

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);
Sign up to request clarification or add additional context in comments.

1 Comment

Or Set<String> otherFruits = Stream.of("orange", "kiwi", "melon", "apple", "watermelon").collect(Collectors.toSet()); to be closer to the question’s code.
4

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

Comments

1

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.

Comments

0

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);
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.