In this lesson, we're going to take a look at the streaming support in Java.utility library. We're gonna take a look at pipelining, terminal points, filtering, mapping, grouping reducing, ordering statistics, and we'll finish up looking at lets. In this particular lesson, we're gonna take a look at pipeline and terminal endpoints. So if you can see the graphic here, we start off with a stream source, which can be about anything. Typically it's a collection type, a list, a map, a queue. It can be an array, an IO channel. It can be any number of things. And we ask that stream source to present itself as a stream using the stream method. At that point, there are any number of intermediate operations that we can perform on the stream. You might have heard the term map reduce or a map transform. It's that idea. We can filter, sort, convert, map, a lot of different operations that we consider these intermediate operations to be, we can carry out on this stream. Now, nothing actually happens until we reach the terminal operation, which we will see could be, I would like the collection now to be presented as a collection after having been transformed and reduced and whatnot or maybe just an aggregate, a count or a sum, or statistics as we'll see. So the first thing I want to do, just to kinda get going here with the idea, I've got a collection here which happens to be a list of fruits. And you can see them here. And what we might have done before Streams would've been the imperative approach. We might have looped over the list. And in this case, we're looking for each fruit that ends with the letter e. So apple, yes, oranges, no, pineapple, yes, and you can see. So we could say, if the fruit ends with the letter e then add that particular fruit to the filtered list. And then there we go. At the end, we should end up with apple, pineapple., and, well, there were two apples in here. Now using Streams, it's a little more declarative. So we can start off with the same list of fruits that we had just a moment ago. And in this case, we're gonna ask the collection, in this case the list, to present itself to us as a Stream. At that point, we can start firing these intermediate operations. And here you can see I'm going to use the filter method. I'm going to use the lambda expression here, such that I would like to filter the stream to include only those fruits that end with the letter e. And then the terminal operator you see here is to collect whatever made it through the filter here, collect this to a list. And I'm receiving those into something called filteredList. So you can see my assertion is the same as it was before, except I didn't write any loops or I didn't do any ifs or create any temporary lists or add to the temporary list, none of that. I just used the stream support and declarative programming. So you can see it was very efficient. We can handle any size set of elements in the stream, very memory efficient. Nothing happened until we hit the terminal endpoint. And I want to take a look at a couple of other ideas here, one wherein I have a collection that has hundreds of thousands of points, geolocations, in it. And I would like to do a couple things. It looks like, first, I'm going to read all the geolocations. This guy has 30,406 elements in it. What I would like to do now is, with that same collection of geolocation points, is turn it into a stream. In other words, ask it to present itself to me as a stream. And then I'm going to issue this filter intermediate operator. I'm going to filter out only the geolocations where the population was greater than 50,000 and then collect it to a list. So you can see here that the largest populations, there were only 951 geolocations out of 30,406 that had more than 50,000 population. Now the next notion down here, we've been using the .stream to turn the collection, in this case, into a stream. If we have hundreds of thousands or millions or tens of millions of elements in the stream, we can just as easily use the parallelStream operation. And what that does is it goes out to see how many cores you have, how many processors on the machine, and it will create under the rug as many threads as there are cores on your platform. And then what it does is fork/join or something along those lines such that it can go out and determine how many populations have greater than 50,000 population using as many threads as you have cores on your machine. So it allows for very easy parallel computations without having to worry about multi-threading and doing all of these things ourselves. And the last thing I'm just gonna show you, I thought this was interesting. If you'd kind of like to poke in and see what it looks like at various points along your pipeline, they've given us a peek method. Now, the only thing I can understand about this is they put this in here for developers. So I'm gonna filter out my fruit stream here, those fruits that end with the lowercase letter e and then the peek expects you to pass it a consumer. So my consumer is, I'm going to consume the elements that made it through this part of the filter, through this part of the pipeline, and print it out. And then I'm going to map whatever fruit made it through that filter to the name of the fruit in all upper case. And you can see we can put these peek operations in there at any number of places along the pipeline. And then, ultimately, I collect it to a list and you can see that what I should have in the final result is APPLE, PINEAPPLE, and APPLE again in all uppercase, because it was filtered and it was transformed or mapped to the uppercase version. So I appreciate you watching. Thank you very much.