Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spliterator state after "consumed" in Stream

I think I've run into an issue with an assumption I made: if a spliterator's item isn't consumed by a stream, the spliterator will still be able to advance to it. It seems like this is not the case.

Here's some code to demonstrate:

import java.util.Spliterator;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * Created by dsmith on 7/21/15.
 */
public class SpliteratorTest {
    public static void main(String[] args) {
        System.out.println("Test 1");
        test1();

        System.out.println("Test 2");
        test2();
    }

    public static void test1() {
        final Spliterator<String> spliterator1 = Stream.of("a", "b", "c", "d", "e", "f").spliterator();

        StreamSupport.stream(spliterator1, false).
                limit(3).
                collect(Collectors.toList());

        System.out.println("spliterator1.estimateSize() = " + spliterator1.estimateSize());
    }

    public static void test2() {
        final Spliterator<String> spliterator1 = Stream.of("a", "b", "c", "d", "e", "f").spliterator();
        final Spliterator<String> spliterator2 = Stream.of("1", "2", "3", "4", "5", "6").spliterator();

        Stream.of(StreamSupport.stream(spliterator1, false), StreamSupport.stream(spliterator2, false)).
                flatMap(Function.identity()).
                limit(3).
                collect(Collectors.toList());

        System.out.println("spliterator1.estimateSize() = " + spliterator1.estimateSize());
        System.out.println("spliterator2.estimateSize() = " + spliterator2.estimateSize());
    }
}

This outputs:

Test 1
spliterator1.estimateSize() = 3
Test 2
spliterator1.estimateSize() = 0
spliterator2.estimateSize() = 6

I understand that spliterators can be split... but it seems like non-split, sequential access would intuitively preserve non-consumed items.

In the test2, it seems spliterator1 is fully consumed after the stream. But in test1, it is not fully consumed.

Is there anything I can do to get the desired behavior, or am I just doing something I shouldn't be doing?

like image 523
dsmith Avatar asked Jul 21 '15 20:07

dsmith


1 Answers

This is an implementation detail, but the way flatMap currently works is that it doesn't split into its content or iterate over it one element at a time. It gobbles it all up in one go. If you do Stream.concat(...) instead of flatMap, you should see the difference:

Stream.concat(StreamSupport.stream(spliterator1, false), StreamSupport.stream(spliterator2, false)).
        limit(3).
        collect(Collectors.toList());

System.out.println("spliterator1.estimateSize() = " + spliterator1.estimateSize());
System.out.println("spliterator2.estimateSize() = " + spliterator2.estimateSize());

output:

spliterator1.estimateSize() = 3
spliterator2.estimateSize() = 6

am I just doing something I shouldn't be doing?

In short, yes. Streams library makes no promises in what state it will leave a spliterator after a partial traversal.

like image 105
Misha Avatar answered Sep 18 '22 18:09

Misha