alright so continuing on with streams now we wouldn't be able to use a map method
with a method that expects 2 arguments i'm talking about the code on line
30 because that method can be mapped to a function now the map method wants a
function not a by function and of course we don't want to have to pass a method
reference and we can also pass a lambda that matches the function pattern
accepts one argument returns one value in this case it just happens to be a method
in the string class that does what we want to do obviously the method that we
use has to work with the item types in the stream now we couldn't use a string
method if the items in the stream weren't strings for example so the map
method ultimately returns a stream the stream will contain all the uppercase
bingo numbers in other words it will contain all the results of calling the
two upper case method on the input stream now it's called map by the way
because it's essentially mapping each item in the input stream to the results
returned by the function argument now the resulting stream will have the same
number of items as the source stream in this case the source stream is the
result of the some bingo number . stream call now remember when we chained
together functions and a function in the chain would operate on the result from the
last function in the chain
essentially this is what's happening here each stream operation operates on
the stream result from the last step now so far we've created a stream from our
collection and passed it to the next operation in the chain map in this case
and now we have a stream containing uppercase bingo numbers next thing we
want to filter the stream so that we end up with a string that contains only
those numbers that start with g so we're going to use the filter method that's
the code on line 31 to accomplish this
now the filter method wants a predicate not a function so in this case we're
passing a lambda expression that takes one parameter and returns a true false
value resulting stream will then contain only those items for which the predicate
return true so at the end of this operation
will have a stream that contains ultimately the 5 g numbers
alright so moving on in the next step we sort the items by calling the sorted
method so this method sorts based on the natural ordering of the items in the
stream now there's also a version of the sorted method that accepts the
comparator parameter but we don't need to provide one in this case the natural
ordering would suffice but again the options is their if you choose to do that then
finally on line 33 we're printing out the results using the for each method but
important note here this for each method isn't the same one that we previously
used with the iterable interface in this case we're using the for each method
from the stream class and in fact all the steps have used methods from the
stream class which if you think about it should make sense and that's because the
object type return from the stream method is a stream and therefore from
that point forward every method we call operates on a stream namely the stream
returned from the previous step
so the stream for each method does what the iterator one does essentially it
accepts a consumer as a parameter and evaluate the consumer for each item in
the stream now since the system . out dot print line method accepts an argument
and doesn't return a value we can map that to consumer that's what we're doing
on line 33 we can then pass it to the for each method and we've done so using a
method references you can see so in previous videos in this section we saw
that the iterator . for each method takes a consumer argument so you might
be wondering could we have passed in a method reference so it's not something
like this like a code like this something we worked on previously so...
so my question could we have done this system....
.....could we have done something like that
absolutely we would have but we needed to learn how lambda expressions map to the
interfaces in the java.util. function package before we started to
use shortcuts which is why we did something along these lines
...which is sort of the longer version of what we got on the previous line rather than
doing what we can essentially do now that we have learned how to do it which is....
.....and using a method reference.....now since the for each
method doesn't return a value the chain has to end here because we're on the
last line
there's nothing to pass on to another step at this point again because the for
each method doesn't return a value so basically there's nothing to
pass on to another step now because of that the for each method is called a terminal
operation so a terminal operation returns either void or non stream
result now since every operation in a chain requires a source stream
ultimately the chain has to end when we use a terminal operation now operations
that return a stream are called intermediate operations because they don't force the end
of the chain
alright so let's have a recap on what our string chain does and look at the results
of each step so firstly when the chain is evaluated a stream pipeline is
created
now the stream pipeline consists of a source zero or more intermediate
operations and then a terminal operation now in our example we use the collection
as the source but we could also have been or it could have been an array or an
input output channel and we can build streams from scratch
the items in the source enter the pipeline and the chain result emerges as
the other end or at the other end of the pipe now as we've seen elements may be
removed from the stream as a result of the operation
so the set of elements that comes out at the other end of the pipe doesn't have
to match the number that entered the pipe
also notice that when we weren't using streams our g64 number is printed with a
lowercase g but when we use streams it was printed with an uppercase G in the
non stream case we didn't use the result of the to uppercase call and that was
the reason for that and when an item passed the test we assign the original
string with the lowercase g to the new list
but in the stream case the map method maps each source string to the function
result so therefore the uppercase string is added to the resulting stream and
ultimately passed to the next step in the chain so that's why the non stream
case prints a lowercase g and the stream case prints and uppercase G
alright so let's have a look at what else we can actually do with streams so
rather than creating a stream based on a collection we can create one from
scratch so let's actually look at doing that now we are going to be using a stream .
of method to create a stream comprised of some numbers from our bingo numbers
list now we could do it by changing the stream chain we've just discussed but i
want to show you how to use the of method instead let's look at doing that
I'm gonna type...
.....so that gives us a
stream of string objects now we can create streams composed of any type of
object but we can't create a stream of mixed types so for example we couldn't
have a stream of string and employee objects right so let's create
another stream that contains the i and n bingo numbers...
....
...
....so let's now concatonate this streams together and we
can use the concat method to actually achieve that
so we can type....
.....note that the concat method
is static
so we can't use it in the chain but we can use the result as the source for a
chain so lets actually print out the number of items in the concatenated
stream we can do that with a print out system . out . print ln and we can do....
.....now since count returns a long value it's a terminal
operation when we run we should see the number 10 printed on the console
which is the total count of both streams you can see 10 showing alright so right now
our stream contains duplicate numbers but we can remove them using the
distinct method which uses the equals method to determine which objects are
duplicates so in this case the stream contains string objects so the methods
going to use the string . equals method for comparison purposes we're
going to change the distinct and the count operations together so that we are
printing at the number of items that remain after the duplicates are removed
so going back in here to this concat stream . count i'm going to put .
.....and if we run that we
should get the number of unique elements you can see we've got the result there
of 6
because it's removed the duplicates essentially the four items have been
removed in this case so question here can we print out the items in a stream
but without ending the chain so let's just say that we're trying to debug a
long chain we wanted to print the results of each operation in the chain
now we know that we can use the for each method that's actually a terminal
operation so instead we could use the peak method which accepts a consumer
argument now it evaluates the consumer for each item in the source stream and
then adds the item to a new stream which then returns now since it returns a
stream value it's an intermediate operation in other words the chain
doesn't have to end so let's try it so right now we're printing at the number
of distinct items so lets actually print the items out before we call
count so that we can verify that the items in the stream are actually
distinct we're going to print out also a dividing line so that we can separate
what's being printed from this chain from any other output let's try that
so here I'm going to just put.....
....
.....
....
...
.....
....
.....
alright so let's actually try running that you can see we've got the six
distinct items in the stream that was returned from distinct followed by the
count now the documentation states for the peak method it mainly exists to
support debugging and that's obviously what we're using it for here to be able to
print that out as we go through and show the elements that actually remain after the
distinct it was called alright I'm going to finish the video here now in the next
video will continue and will actually look at the flat map method i'll see you
in the next video