5

I was wondering how stream().toArray[x -> new Integer[x]] knows what size of array to from? I wrote a snippet in which i created a list of an integer of size 4 and filtered the values and it created an array of length of the filtered stream, I could not see any method on stream to get a size of the stream.

List<Integer> intList = new ArrayList<Integer>();
intList.add(1);
intList.add(2);
intList.add(3);
intList.add(4);


    Integer[] array = intList.stream()
                             .filter(x -> x > 2)
                             .toArray(x -> {
                                System.out.println("x --> " + x);
                                return new Integer[x];
                              });

System.out.println("array length: " + array.length);

Output of above code:

x --> 2
array length: 2

initially, the snippet was like

Integer[] array = intList.stream()
                 .filter(x -> x > 2)
                 .toArray(x -> new Integer[x]);

Just to get the understanding what value of x it passes i had to change it to print x in lambda

8
  • toArray is a terminal operation. When you get to toArray, it evaluates the whole stream, and at that point knows how many items it has. Commented Feb 16, 2017 at 10:09
  • I know toArray was a terminal operation but i was interested in how it knows the size of stream?? I was thinking Stream Api would have a method called size() which i couldn't find? Commented Feb 16, 2017 at 10:17
  • 2
    @Loks It's called count() Commented Feb 16, 2017 at 10:19
  • 3
    Instead of x -> new Integer[x] you can write Integer[]::new ;) Commented Feb 16, 2017 at 10:21
  • 1
    For the record : you might want to consider accepting the most helpful answer at some point. Commented Jun 15, 2017 at 4:14

3 Answers 3

7

Of course, this is implementation dependent. For some streams, the size is predicable, if the source has a known size and no size changing intermediate operation is involved. Since you are using a filter operation, this doesn’t apply, however, there is an estimate size, based on the unfiltered count.

Now, the Stream implementation simply allocates a temporary buffer, either using the estimated size or a default size with support for increasing the capacity, if necessary, and copies the data into the destination array, created by your function, in a final step.

The intermediate buffers could be created via the supplied function, which is the reason why the documentation states “…using the provided generator function to allocate the returned array, as well as any additional arrays that might be required for a partitioned execution or for resizing” and I vaguely remember seeing such a behavior in early versions. However, the current implementation just uses Object[] arrays (or Object[][] in a “spined buffer”) for intermediate storage and uses the supplied function only for creating the final array. Therefore, you can’t observe intermediate array creation with the function, given this specific JRE implementation.

Sign up to request clarification or add additional context in comments.

Comments

4

The thing is: this is a terminal operation. It happens in the end, when the stream was processed: meaning - the "final" count is known by then; as there are no more operations that could remove/add values to the stream!

2 Comments

I was thinking how it knows the final count? Is it the way functional programmer think rather not worrying about how it is done?
@Loks it creates a collection which contains all the references, then it knows the length and calls this lambda.
4

Simply look at javas stream documentation of toArray.


<A> A[] toArray(IntFunction<A[]> generator)

Returns an array containing the elements of this stream, using the provided generator function to allocate the returned array, as well as any additional arrays that might be required for a partitioned execution or for resizing.

This is a terminal operation.

API Note: The generator function takes an integer, which is the size of the desired array, and produces an array of the desired size. This can be concisely expressed with an array constructor reference


Therefore toArray does give you the desired array size as a parameter and you are responsible for allocating a correct sized array, at least when using this method. This method is a terminal operation. So the size calculation is done within the internals of the Stream API.

IMHO it is better to grasp if you name your lambda parameters differently for filter and toArray.

Integer[] array = intList.stream()
                 .filter(myint -> myint > 2)
                 .toArray(desiredArraySize -> new Integer[desiredArraySize]);

6 Comments

You can also write .toArray(Integer[]::new).
But then you don't use the size parameter, do you.
Actually I do. Integer[]::new is unary function taking an int as parameter.
Cool. Thx for clarifying that.
@ Florian F then what is the value of the int parameter in Integer[]::new
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.