Towards the end of 2015, we set free our cheat sheet for Java 8 best practices, which got thousands upon thousands of hits and downloads! So we thought, we should spawn another one, and we did just that. This point around, based on your analysis feedback, we focus on one of the greatest and generous features in the Java 8 release, the Streams API. You should click on the rascal sheet below and print it out. It will look awesome next to the Java 8 cheat sheet! Incidentally, Venkat Subramaniam, Java legend gave a Java 8 Streams masterclass just last week on Virtual JUG, so make sure you review that video out if you want to hear audit rush streams in more depth.
Java-8-Streams-cheat-sheet
This blog post considers the streams API, but if you’re arguing, here’s the 1 page Java 8 current cheat sheet, click on it, save it, print it out!
Let’s start off with what a current actually is and what a flow isn’t! Here are some flow points which shape how you should assume about a stream:
- A Stream is a pipeline of task that can be evaluated
- Streams can transform data
- A Stream is not a data structure
- Streams cannot mutate data
Most action, a stream isn’t a data structure. You can often create a stream from collections to apply a number of duty on a data structure, but a stream itself is not a data structure. That’s so great, I voiced it twice! A cited can be poised of multiple functions that create a pipeline that data that flows through. This data cannot be mutated. That is to say, the initial data structure doesn’t change. However, the data can mutate and later stored in another data structure or may be consumed by another use.
We state that a stream is a pipeline of functions or operations. These operations can either be the divide as an intermediate operation or an incurable operation. The difference between the two is in the output which the affair creates. If an operation outputs another stream, to which you could apply a more operation, we call it an in between operation. However, if the operation outputs a specific type or produces a side effect, it is a real type. A subsequent stream operation cannot follow an incurable operation, obviously, as a stream is not returned by the last operation!
Intermediate operations
An intermediate affair is always lazily executed. That is to say, they are not run until the point a lethal operation is reached. We’ll look in more measured at a few of the most popular extent operations used in a stream.
- filter – the filter affair returns a stream of an item that satisfies the aspect passed in as a parameter to the operation. The principle themselves before and after the drain will have the same type, however, the number of fundamental will likely change.
- map – the map operation returns a stream of material after they have been refined by the function passed in as a limit. The elements before and after the grading may have a different type, but there will be the same total count of elements.
- distinct – the distinct operation is a special case of the filter figure. Distinct returns a stream of detail such that each element is exclusive in the stream, based on the equals method of the fundamental.
Here’s a table that rehashes this, including a couple of other compile intermediate operations.
Function | Preserves count | Preserves type | Preserves order |
map | Y | N | Y |
filter | N | Y | Y |
distinct | N | Y | Y |
sorted | Y | Y | N |
Peek | Y | Y | Y |
Terminal operations
A terminal operation is trivial eagerly executed. This operation will kick off the hit of all previous lazy operations instant in the stream. Terminal operations either entry concrete types or goods a side effect. For instance, a trim operation which calls the Integer:: sumoperation would production an Optional, which is a detailed type. Alternatively, the forEach affair does not return a specific type, but you are able to add a side effect such as a print out each bit. The collect lethal operation is a special type of fatal which takes all the elements from the tide and can produce a Set, Map or List. Here’s a tabulated summary.
Function | Output | When to use |
reduce | concrete type | to cumulate elements |
collect | list, map or set | to group elements |
forEach | side effect | to perform a side effect on elements |
Stream examples
Let’s take a look at a team of examples and see what our practical code examples using streams would glance like.
Exercise 1: Get the exclusive surnames in uppercase of the first 15 book authors that are 50 years old or older.
So you know, the cause of our stream, library, is an ArrayList. Check out the code and follow along with the f. From this list of books, we first use to map from books to the book authors which gets us a stream of definition. From this list of books, we first wish to sketch from books to the book architect which gets us a flow of Authors and then drain them to just get those authors that are 50 or over. We’ll picture the last name of the Author, which plan us a stream of Strings. We’ll map this to uppercase Strings and make sure the view are exclusive in the stream and grab the first 15. Finally, we arrival this as a list using toListfrom.java.util.streams.Collectors
Did you notice that as we read through the code we can represent what it’s doing line by line? Imagine doing that with imperative code!
Exercise 2: Print out the sum of ages of all female authors younger than 25.
Using the same original stream we once again map the fundamental from Books to Authorsand filter just on those authors that are female. Next we map the elements from Authors to author ages which give us a tide of ints. We filter ages to just rush that are less than 25 and use a reduce operation and Integer:: sum to total the ages.
Parallel Streams
You can parallelize the work you do in a stream in a couple of ways. Firstly you can get a parallel stream precisely from your source by calling the parallelStream() method directly as shown:
Alternatively, you can call an intermediate operation on an existing stream which spawns off threads and finishes further operations in parallel, as shown:
One important point to note is that parallel flow achieves parallelism through threads using the existing common ForkJoinPool. As a result, there are possible complications as we detailed in this previous RebelLabs post. Using parallel streams can cause applying issues depending on what you’re doing in your stream as well. Make sure you want to use a parallel stream for a big applying job, rather than using them by default. Also, given you’re using the common ForkJoinPool, be sure not to run any prevent operations.
A classic example of a potential concurrency issue when using coordinate streams is when updating a shared mutable variables from a forEach function. Let’s consider the following code:
Would you expect multiple threads to concurrently approch an ArrayList without issue normally? Of course, you wouldn’t! So you shouldn’t await it to within a parallel stream. In fact, it’s best not to do this even in a non-parallel stream, just in case official tries to make it parallel in future. It tends to be protected to collect this into a List using the collect()operation.
Anyway, don’t want to hold you any intact, I know you’re pretty converted by now that Java 8 streams are controlling to the best practices of evolution and you can get all the instruction about them in this concise 1-page cheat sheet!
This is our second cheat sheet, after the Java 8 Best Practices Cheat Sheet. Our next one is earlier in the works, due to the demand and demand! This one will be over some of the most useful git duty known to man, and woman! Make sure you preserve an eye on the RebelLabs blog so you don’t miss it! You could even subscribe to our put list so that you get a gentle nudge when it’s available.