This week's book giveaway is in the NodeJS forum.
We're giving away four copies of Serverless Applications with Node.js and have Slobodan Stojanovic & Aleksandar Simovic on-line!
See this thread for details.
Win a copy of Serverless Applications with Node.js this week in the NodeJS forum!
  • 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 all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Liutauras Vilda
  • Bear Bibeault
  • Jeanne Boyarsky
  • paul wheaton
Sheriffs:
  • Junilu Lacar
  • Paul Clapham
  • Knute Snortum
Saloon Keepers:
  • Stephan van Hulst
  • Ron McLeod
  • Tim Moores
  • salvin francis
  • Carey Brown
Bartenders:
  • Tim Holloway
  • Frits Walraven
  • Vijitha Kumara

Traversing a stream of Characters  RSS feed

 
Ranch Hand
Posts: 195
2
  • Mark post as helpful
  • send pies
  • 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!
 
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • 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: 63849
209
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • 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: 63849
209
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • 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
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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: 63849
209
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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: 63849
209
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Likes 1
  • Mark post as helpful
  • send pies
  • 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
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think you've got it.
 
Biniman Idugboe
Ranch Hand
Posts: 195
2
  • Mark post as helpful
  • send pies
  • 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
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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: 195
2
  • Mark post as helpful
  • send pies
  • 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
Saloon Keeper
Posts: 9997
208
  • Mark post as helpful
  • send pies
  • 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.
 
See ya later boys, I think I'm in love. Oh wait, she's just a tiny ad:
global solutions you can do at home or in your backyard
https://www.kickstarter.com/projects/paulwheaton/better-world-boo
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!