• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • paul wheaton
  • Liutauras Vilda
  • Ron McLeod
Sheriffs:
  • Jeanne Boyarsky
  • Devaka Cooray
  • Paul Clapham
Saloon Keepers:
  • Scott Selikoff
  • Tim Holloway
  • Piet Souris
  • Mikalai Zaikin
  • Frits Walraven
Bartenders:
  • Stephan van Hulst
  • Carey Brown

Traversing a stream of Characters

 
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The codes below were excerpted from pages 227 to 231 of Java 8 in Action: Lambdas, streams, and functional-style programming
Raoul-Gabriel Urma, Mario Fusco, and Alan Mycroft
Note:  I inserted the main() method so that I can compile and run the program.


I have several questions to ask about the codes above.  I will like to ask the questions one at a time so I do not mix up things.
Before now, I thought that creating an instance of a class decouples the instance from the class; something like making a copy of the class
without modifying the class.  In the WordCount class definition, we have:

Their actual values are supplied while instantiating the class. The values remain the in the class until the next instance is created. This means
the current instance sets some fields in the class that could determine the values in the fields of the next instance. So the question is, are
final fields final in the class or final in the instance of the class?
A related question: Is the instance of a class independent of the class or are there underlying unbreakable connections between a class and its instances?
Static fields and static methods on my mind!
 
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
What makes you think that the values of counter and lastSpace are stored in the class?

The accumulate() method calls the WordCounter constructor with counter+1. The combine() method calls the WordCounter constructor with counter + wordCounter.counter. These accumulate() and combine() methods are instance methods, and they're accessing instance fields.
 
Marshal
Posts: 80123
416
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Biniman Idugboe wrote:. . . Before now, I thought that creating an instance of a class decouples the instance from the class; something like making a copy of the class without modifying the class.

No, you don't copy the class. The class constitutes instructions to make an object, and the object is made from those instructions. An object is not a copy of the class, but an implementation of what the class represents.

. . . Their actual values are supplied while instantiating the class. . . . are final fields final in the class or final in the instance of the class?

You mean the class has been designed to be immutable? So you have a word count with value 3 and you count another word and you get another object with the value 4. Does that answer your question about where instance fields are stored? Can't you see that line 30 creates a new counter object?

A related question: Is the instance of a class independent of the class or are there underlying unbreakable connections between a class and its instances?
Static fields and static methods on my mind!

Of course the relationship between a class and its instances is unbreakable. The class determines the structure of the object and changes to the code in the class may cause the program to faill: serialisation and deserialisation is the one example that springs to mind.
The object contains all its instance fields, but there are several other things stored in the object which behave as if they were instance fields. They include a reference to the Class<T> object corresponding to the class the object was made from. That Class<T> object contains all the methods, and the static fields. Another part of memory in the object stores an implicit reference to the object itself.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you Sir.  I am thinking the current instance stores a value in counter because each time an instance of the class is created, the value in counter increases by 1.
First instance, the value in counter is 1.
Second instance, the value in counter is 2.
Third instance, the value in counter is 3 and on till the value in counter is 19.
If the value of the current instance is not stored in the class, then the value in counter + 1 should remain at 1. I am just thinking.  I need clarifications.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I modified the accumulate() method as follows:
Note that the line numbers do not match those in the original codes above.

Here's the print out.
1
1
1
2
2
2
2
2
3
3
3
4
4
4
4
4
4
5
5
6
6
6
6
6
6
7
7
7
7
8
8
9
9
9
9
9
9
9
9
10
10
11
11
11
12
12
12
12
12
13
13
13
13
13
13
14
14
14
14
15
15
16
16
16
16
16
16
17
17
17
18
18
18
19
19
19
19
19
19
19
19
The number of words in the sentence is: 19
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Mr Campbell. If the default value of an int is zero and the current instance does not store value in the class, then line 30 should produce an instance whose counter has a value of 1. How come the current instance seems to add to the value of the preceding instance?
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Let's look at a simplified version of the String class:

So, just because the new string is based on the value of the old string, does that mean that the character array is stored in the String class? No, the old string object just passes a modification of its current value to the constructor of the new string object.

The accumulate() and combine() methods WordCounterSpliterator do the same thing. They just modify the current value and pass it to the constructor of the new object.
 
Campbell Ritchie
Marshal
Posts: 80123
416
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Biniman Idugboe wrote:. . . If the default value of an int is zero . . .

But a final field doesn't have a default value. Default values only apply to ordinary fields, not to all ints.
Stephan has already answered the rest of your question.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Are saying that a final field does not have initial value?  Then how does counter + 1 work?
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You seem to keep overlooking that the value is set in the constructor, by the previous instance. Even if there was a default value, it would be of no consequence, because it would be overwritten by the value passed in the constructor.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I do agree that the value is set in the constructor.  However, I do not see where the constructor makes a reference to the previous instance.
 
So, is counter + 1 effectively equivalent to previousInstance.counter + 1 ?
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The constructor doesn't refer to the previous instance. The previous instance passes its current value to the constructor, which you can see in line 30 and line 36 of the code in the first post.
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Biniman Idugboe wrote:  
So, is counter + 1 effectively equivalent to previousInstance.counter + 1 ?


I think I see your confusion. It appears you believe that the counter identifier in this snippet of code refers to the field of the new object. It doesn't. The snippet you posted is equivalent to the following bit of code:
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

The previous instance passes its current value to the constructor . . .


The value in the field named counter in the original instance continues to increase for every subsequent instance created.  Where is the finality in counter?
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Everytime I do originalInstance.accumulate('a'); the value of counter in originalInstance increases by 1. What is final about counter?
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Then the code that you use to test is wrong. Please show code that demonstrates that the counter in originalInstance is incremented.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I have not tested the last two lines of code I submitted.  If I did and they answered my questions, I will not continue to ask questions that are appearing to be repetitive, maybe even annoying.

It appears you believe that the counter identifier in this snippet of code refers to the field of the new object. It doesn't.


counter + 1 is an executable expression, not a parameter declaration.  Where did counter come from?
No offense, no offense at all!  The code snippet has kept me ...., well, awake in the last few days.  It looks mysterious.
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It refers to the counter field of the current WordCounter object. The expression counter + 1 just returns the value of the counter field plus one, which is then passed to the constructor of the new object. It doesn't increment the counter field.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Oh! bear with me.  So, instance1 leads to the creation of instance2 which leads to the creation of instance3 and so on until the end of the string.  
Is this some kind of self invocation of the accumulate() method?  That is this.accumulate(Character c).
 
Campbell Ritchie
Marshal
Posts: 80123
416
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Biniman Idugboe wrote:Are saying that a final field does not have initial value? . . .

No, I said that unlike ordinary fields, it does not have a default value, not even 0/false/null. That is why the Java® Language Specification (=JLS) says,

A blank final instance variable must be definitely assigned and moreover not definitely unassigned at the end of every constructor . . .

Non‑final fields do not have to be definitely assigned to because they have default values.
Most of your other questions have been answered already.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you Mr. Campbell.  Sure you have read the code snippet from beginning to end.  So, I ask:  What is invoking the accumulate( ) method?  That's not obvious at all.
 
Campbell Ritchie
Marshal
Posts: 80123
416
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The reduce() method takes three arguments; one is the initial state, often 0. It is called identity because it makes no difference to any of the values. The second is a way to add to that state, which is called an accumulator. The third part is the combiner, which combines results should you run your Streams in parallel. The reduce() method starts with the initial state, then every time it receives an element of the Stream, it calls accumulate(). If multiple Streams run in parallel, the combiner combines their results when each Stream terminates. More details in the documentation for reduce().
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Wow!  

So, in the new WordCounter(counter + 1, false) inside the accumulate( ) method, the counter refers to the identity.counter. Forgive me for saying identity.counter.
The result of new WordCounter(counter + 1, false) becomes the identity for the next instance and so on. Well, it makes sense now.
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think you've got it.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Permit me to ask another question.  At what points are the tryAdvance( ) and the trySplit( ) methods invoked?  I am assuming that the reduce( ) method is responsible for invoking the methods.  Looks like the reduce( ) method does a lot behind the scene.
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Each intermediate stream operation is implemented in a spliterator. When you call two intermediate operations after each other then you create a chain of two spliterators where one depends on the other. For instance, when you first call filter() and then call map(), the tryAdvance() method of the spliterator associated with map() will create an action that represents the mapping operation, and pass it to the tryAdvance() method of the spliterator associated with the filter() operation, which will call that action after it has its own action handed even further down the rabbit hole.

So really, the tryAdvance() method is the thing that actually performs a particular operation in the chain of stream operations. The chain has a start and an end. The start of the chain is a spliterator that retrieves elements out of a data source. The end of the chain is a terminal operation that initiates the first call to tryAdvance().

The spliterator given in the code that you posted retrieves elements from a data source. In this particular case, it retrieves characters from a string. Each time the next operation in the chain of operations calls its tryAdvance() method, it gets the next character from the string, and passes it to the action that was passed to it by the next operation in the chain. The lines 53 and 54 do almost the exact same thing as calling String.chars(), except that it creates a Stream<Character> instead of an IntStream.

Terminal operations are not implemented by spliterators. Instead, they use the last spliterator in the chain and repeatedly call the tryAdvance() method until there are no more elements. For parallel streams, they may also call the trySplit() method, more about that later.

If we don't take parallel streams into account, here is a example of what a naive implementation of reduce() might roughly look like:

The above code repeatedly calls tryAdvance() and for each element remaining it will update the result by applying the accumulator function to it.

This works fine for sequential streams, but there are also parallel streams. Parallel streams need to be able to process multiple elements in different threads. To prevent different threads from processing the same elements, the source of elements is split by calling trySplit() on the spliterator. This causes the spliterator that it was called on to only process half of the elements that is was initially supposed to process, and returns a new spliterator that processes the other half. Then tryAdvance() is called on one spliterator repeatedly in one thread, and tryAdvance() is called on the other spliterator repeatedly in another thread. After two spliterators are done, then their results are combined with the combiner function.

Again, here is a naive implementation:
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you so much Mr. Stefan. I have barely glanced at your latest response because I am currently pinned down by several activities that crossed into my schedule. When I regain normalcy, I will carefully read the response and perhaps ask more questions.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Mr. Stephan, please, tolerate me.  I am kind of embarrassed to say that I am not up to speed with this spliterator aspect. In fact, not up to speed in most aspects.

when you first call filter() and then call map(), the tryAdvance() method of the spliterator associated with map() will create an action that represents the mapping operation,
and pass it to the tryAdvance() method of the spliterator associated with the filter() operation . . .


So, if I have the following:

Does it mean that the operations are performed in reversed order?


Nice if you could explain the syntax a bit.
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
If you want to address me formally, you can say 'Mr. van Hulst', but I prefer just 'Stephan'.

Don't be embarrassed. Spliterators are not intuitive and a lot of experienced programmers aren't familiar with how they operate exactly. Besides, I enjoy trying to answer your questions.

Operations aren't really performed in reverse order. They are initiated in reverse order, but they finish in normal order. You can compare it to how nested method calls work. The main() method of a program starts running first, and may call another method. Only after that method call returns, the main method can return.

In the example code that you gave, nothing happens at all because there is no terminal operation. But let's assume that you added the terminal operation .forEachOrdered(System.out::println).

The forEachOrdered() method would call the tryAdvance() method of the map operation, which in turn calls the tryAdvance() method of the filter operation, which in turn calls the tryAdvance() method of the source. The source takes the integer 2 and passes it to the Consumer that the filter operation passed to it. That Consumer checks that 2 is greater than or equal to 3, determines that it's not, and so does nothing more. Control returns to the tryAdvance() method of the source stream, which just returns the value 'true' to indicate to the filter operation that there are more elements remaining, which the filter returns back up to the map operation, which the map operation returns to the forEachOrdered operation.

Because there are more elements remaining, the forEachOrdered operation calls the tryAdvance() method of the map operation a second time. This will call the tryAdvance() method of the filter operation. This will call the tryAdvance() method of the source. The source passes the integer 4 to the Consumer that was passed in by the filter operation. The Consumer checks that 4 is greater than or equal to 3, determines that it is, and so it passes 4 to the Consumer that was passed in by the map operation. The map's Consumer then applies the toString() method on the 4, and passes the String "4" to the Consumer that was passed in by the forEachOrdered operation. The forEachOrdered operation's Consumer then calls System.out.println() on "4".

Maybe it's easier to visualize if I write this particular chain of operations as nested method calls:

Note that this is NOT what really happens. It's just a way you can imagine how the control is transferred between the different spliterators. You can see that even though the flow is initiated from the last stream operation to the first, the first stream operation will be finished before the last stream operation is finished.
 
Marshal
Posts: 8988
652
Mac OS X Spring VI Editor BSD Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Biniman Idugboe,

Cow-gratulations, your topic have been published in our April's journal as an interesting one.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Vilda for awarding me a cow.  I am one step closer to running a diary farm.  I am hoping this award has not closed the thread.  As I said earlier, my schedule has been largely shattered in recent time and will be for a few more days.  So, it has been hard for me to catch up on this thread.
Thanks Stephan for your commitment and patience.  I really appreciate them.  Now, when a Spliterator<T> is instantiated on a collection, I imagine that the collection is transformed into another form with tools to navigate and/or partition the new form.  I imagine that the initial collection would cease to be a collection.  But a spliterator is not a collection, so what happens to a collection when it is used to instantiate a Spliterator?
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Nothing happens to the collection. You just create a new object of some spliterator type that has access to the internals of the collection. Here's a very simple example of a collection where I've implemented the spliterator() method myself:

When you create a stream from an instance of this collection, the stream will at some point call spliterator() which creates a new Spliterator instance. The tryAdvance() method of the spliterator has access to the internals of the collection as you can see in the switch statement on line 32, but the original collection is not changed in any way nor is it thrown away.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
In the illustration above, the Collection encapsulates the spliterator. So, the fields and methods of the collection are visible to the spliterator.  Can we say that the collection wraps the spliterator?  Could you also illustrate how, for example, a map() method becomes a spliterator, that is, how a spliterator wraps a map() method?
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Okay, So, I have tried to use the above class. See the codes below.  Please, do not punch me in the nose. Even I, an aspiring dairy farmer with only three cows knows that the codes are clunky.



The question is: What is the connection between the map() method and the instance of ThreeThings?  I am asking this question because the instance of ThreeThings ( a collection) has already been transformed into a stream so to speak and a stream is not a collection. At what point does the map() method invoke the spliterator() method of threethings?
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Biniman Idugboe wrote:In the illustration above, the Collection encapsulates the spliterator. So, the fields and methods of the collection are visible to the spliterator.  Can we say that the collection wraps the spliterator?  Could you also illustrate how, for example, a map() method becomes a spliterator, that is, how a spliterator wraps a map() method?


You can say that the collection encloses the spliterator.

For an operation like map(), the stream encloses a spliterator that has access to the internals of the stream: The spliterator that represents the previous operation in the pipeline. It looks roughly like this:


Biniman Idugboe wrote:At what point does the map() method invoke the spliterator() method of threethings?


It doesn't. The stream() method of a collection calls spliterator(). It probably looks a bit like this:
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you.  Now I see how the map() method encloses a spliterator.  However, in the use case codes I presented above, there was no previous operation to the map() method and there was no next operation.  So, what is happening in this case?  Also, I am confused about the consumer supplied to the tryAdvance() method. Where is that coming from (what is supplying it)?
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The "previous operation" that comes before your map() operation is the Spliterator that retrieves elements from the collection. It's the new spliterator that is returned on line 23 of the ThreeThings class.

The "next operation" that comes after your map() operation is the Consumer that is passed to the map() operation by the forEach() operation that you call on line 13 of the ThreeThingsInUse class.
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Biniman Idugboe wrote:Also, I am confused about the consumer supplied to the tryAdvance() method. Where is that coming from (what is supplying it)?


It's supplied by the 'next' stream operation.

Look at the map() operation I implemented in the StreamImpl class. It returns a spliterator that implements the tryAdvance() method. It also calls the tryAdvance() method of the spliterator that represents the previous operation. As you can see, chaining stream operations just creates a daisy-chain of spliterators that each call the tryAdvance() method of the previous spliterator by passing a Consumer.
 
Biniman Idugboe
Ranch Hand
Posts: 353
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
From the StreamImpl definition, the map() method is an instance method.  I suppose that means it must be invoked on an instance of StreamImpl.
The MappingSpliterator is enclosed in the map() method.  I am thinking that when the map() method is invoked, the codes inside its body are executed.  But all the methods of MappingSpliterator are instance methods, right? Are these methods going to be invoked? Invoked on what?
In the statement in line 44:

What is supplying the spliterator?
At what point is the tryAdvance() method invoked?  
 
Stephan van Hulst
Bartender
Posts: 15737
368
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Biniman Idugboe wrote:The MappingSpliterator is enclosed in the map() method.  I am thinking that when the map() method is invoked, the codes inside its body are executed.


No. When map() is invoked, it only creates an instance of MappingSpliterator, with which an instance of StreamImpl is created. That instance of StreamImpl is returned to the caller of the map() method. No istance methods of the spliterator are called until a terminal operation is performed.

But all the methods of MappingSpliterator are instance methods, right? Are these methods going to be invoked? Invoked on what?


Invoked on the spliterator that is wrapped inside the stream that is returned from the map() method. These methods will be invoked by the next spliterator in the chain, but only when a terminal operation is performed.

In the statement in line 44:

What is supplying the spliterator?


It's the instance field that the StreamImpl instance was constructed with. So whatever creates an instance of StreamImpl is what's supplying the spliterator. You can see that the default stream() operation of Collection supplies a spliterator for the initial stream in the pipeline. Each stream operation supplies a new spliterator for the next chain link in the pipeline. So while map() uses the spliterator that was supplied by the previous stream operation, it supplies a new MappingSpliterator that will be used by the next stream operation.

At what point is the tryAdvance() method invoked?


When the tryAdvance() method of the next spliterator in the pipeline calls it. The last spliterator in the pipeline has its tryAdvance() method called by a terminal operation, such as forEach() or reduce(). Terminal operations don't create a new spliterator. They initiate the chain of calls to tryAdvance().
 
Politics n. Poly "many" + ticks "blood sucking insects". Tiny ad:
Smokeless wood heat with a rocket mass heater
https://woodheat.net
reply
    Bookmark Topic Watch Topic
  • New Topic