java-8

JAVA 8 – Features

Today I bring you to this blog an entry about the improvements of Java 8. I have to confess, that although at first it seemed a real disorder, with the course of the hours and above all, with the practice of new structures, I have been convincing that the code is really much more readable, comfortable to implement and most importantly: efficient.

It is true that I have a lot to try, learn and strengthen, but there are structures and ways of use that have made life easier for me.

So, perhaps the same thing can also serve you.

Stream, with you started everything in Java 8!
Unlike its predecessor, Java 7, its latest version (Java 8) has added the stream method to the Collection interface (from the java.util package).

/**
 * Returns a sequential {@code Stream} with this collection as its source.
 *
 *

This method should be overridden when the {@link #spliterator()}
 * method cannot return a spliterator that is {@code IMMUTABLE},
 * {@code CONCURRENT}, or late-binding. (See {@link #spliterator()}
 * for details.)
 *
 * @implSpec
 * The default implementation creates a sequential {@code Stream} from the
 * collection's {@code Spliterator}.
 *
 * @return a sequential {@code Stream} over the elements in this collection
 * @since 1.8
 */
 default Stream stream() {
 return StreamSupport.stream(spliterator(), false);
 }

Java 8
What is Stream?

It is sequence of elements. With this method we can transform a collection of objects (arrays, lists, …) into a series of objects.

We will leave testimony of how this method would be used for ArrayList or Array classes:
The List interface inherits from Collection so any type that implements it can also make use of its stream method so it is easy to deduce the following:

List listaCadenas = new ArrayList(); //Notación diamante

List listaCadenas = new ArrayList();
 listaCadenas.add("Juan");
 listaCadenas.add("Antonio");
 listaCadenas.add("Maria");
 Stream streamCadenas = listaCadenas.stream();

In the case of Arrays we can also ese the method:
 public static Stream stream(T[] array) {
 return stream(array, 0, array.length);
 }

Integer[] enteros = {1,2,3,4,5,6};
 Stream enterosStream = Arrays.stream(enteros);

Besides the stream method Java 8 includes another method in the line: parallelStream. For more information:
 https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html#stream--

Perfect, but with this, what do we do? Shhh, keep calm … step byo step, smooth smooth … let’s continue.

Map, your ally

Although the Stream interface provides several methods, we start with this: map. Although in the following entries we will not forget: filter, flatMap and reduce.

How many times have we go over a structure where, checking for a condition, we performed one action or another? How many lines of code? How many nested for / if / else of which Sónar complained (and ourselves a few weeks after we forgot what that fragment code did)?

Well in Java 8 we are allowed to do that same in a line.

As before the first thing is to introduce the method:

/**
 * Returns a stream consisting of the results of applying the given
 * function to the elements of this stream.
 *
 *

This is an intermediate
 * operation.
 *
 * @param The element type of the new stream
 * @param mapper a non-interfering,
 * stateless
 * function to apply to each element
 * @return the new stream
 */
 Stream map(Function mapper);

Map will apply a function that we will call F on each of the elements of the sequence and will return another sequence of elements already modified.

And that function is …?

Whatever we need to apply, that does comply with some restrictions.

For example, we can say that we want to trim () to each element of the String sequence of the sequence of the first element. For this we can write it in Java 8:

streamCadenas.map(s->s.trim());

It is the way I have started using it and with it I continuo although it is not the only one.
In this option we make use of a variable s, whose scope is that of the method (outside of that map it will not exist and it will not be necessary to declare it previously).

We are indicating that each element of that sequence, we will save it in a variable s, of the same type of element, and we will apply the function trim ().

Why have not we put String s-> s.trim ()? Because Java fully recognizes the type of the variable s when knowing the type of elements of the sequence (Stream). If we had used the Stream inteosStream s it would be of type Integer and neither would it be necessary to declare it.

Another way to do the same would be:

streamCadenas.map(String :: trim).

The difference with the previous one is that we saved the variable, this nomenclature is not always valid. We must ensure that the method has no parameters, if it had them, it could not be used in this way but in its first version.

Filter, or how to save a few loops

/**
 * Returns a stream consisting of the elements of this stream that match
 * the given predicate.
 *
 *

This is an intermediate
 * operation.
 *
 * @param predicate a non-interfering,
 * stateless
 * predicate to apply to each element to determine if it
 * should be included
 * @return the new stream
 */
 Stream filter(Predicate predicate);

As in the API itself, filter receives a sequence of elements and returns those that meet the searched pattern (predicate).

We start by showing a basic example.

We have a list of chains in which we store types of vehicles and we will get from those that are not “motorbike”.

List vehicles = Arrays.asList("car", "motorbike", "bus");

Before we would do something like this:

List filteredVehicles = new ArrayList();
 for(String vehicle: vehicles){
 if(!"motorbike".equals(vehicle)){
 filteredVehicles.add(vehicle);
 }
 }

With Java 8 would be like this:

List filteredVehicles = vehicles.stream()
 .filter(v -> !"motorbike".equals(v))
 .collect(Collectors.toList());

In our case, already for a project, we had a type list of Profile called profiles where we had instances of several types: INDIVIDUAL, CORPORATE, … we wanted from that list to get three. One for each type of profile of the application.

In the beginning we used for/if in which in for we repeated the profiles list and in the if we specified the condition to be fulfilled. Since there were several lists that we wanted as output, it was necessary to use nested if/else. Finally we managed to do it much cleaner with this Java 8 method. Below there is an example of one of the lists obtained.

In this case, the important thing is to see another type of structure where the filter predicate is another method: isType, a method of its own.

List individuales = profiles.stream().filter(s -> isType(s, EnumTypeCertificate.INDIVIDUAL.name())).collect(Collectors.toList());

private boolean isType(Profile profile,String typeProfile)
 {
 return profile.getType().equals(typeProfile);
 }

flatMap, magia!

/** * Returns a stream consisting of the results of replacing each element of * this stream with the contents of a mapped stream produced by applying * the provided mapping function to each element. Each mapped stream is * {@link java.util.stream.BaseStream#close() closed} after its contents * have been placed into this stream. (If a mapped stream is {@code null} * an empty stream is used, instead.) * * This is an intermediate * operation. * * @apiNote * The {@code flatMap()} operation has the effect of applying a one-to-many * transformation to the elements of the stream, and then flattening the * resulting elements into a new stream. * * Examples. * * If {@code orders} is a stream of purchase orders, and each purchase * order contains a collection of line items, then the following produces a * stream containing all the line items in all the orders: * {@code * orders.flatMap(order -> order.getLineItems().stream())… * } * * If {@code path} is the path to a file, then the following produces a * stream of the {@code words} contained in that file: * {@code * Stream lines = Files.lines(path, StandardCharsets.UTF_8); * Stream words = lines.flatMap(line -> Stream.of(line.split(” +”))); * } * The {@code mapper} function passed to {@code flatMap} splits a line, * using a simple regular expression, into an array of words, and then * creates a stream of words from that array. * * @param The element type of the new stream * @param mapper a non-interfering, * stateless * function to apply to each element which produces a stream * of new values * @return the new stream */ Stream flatMap(Function<? super T, ? extends Stream> mapper);

In the example below we had the following problem. Each Profile element had a string-type token field that contained values separated with commas.

We wanted to get each of these elements and add them to a list but without repeated elements.
To do this with map we transform the list of Profile objects into a succession of elements that were applied to their token variable the split method.

Imagine that we have something like this:

public class Profile {
 String token;
 ...
 //Getters and Setters
 ...
 }

The token field has a value like this => token = “BROWSER, PKCS12, JKS”;
Now imagine that we have two Profile objects we would have something like this:
Profile list

Lista de Profile
 [0] Profile = > token = "BROWSER,PKCS12,JKS,USER_GENERATED,JKS,SOFT"
 ...

What we intend to obtain is this:
String List

Lista de String

[0] "BROWSER"
 [1] "PKCS12"
 [2] "JKS"
 [3] "USER_GENERATED"
 [4] "SOFT"

And even more, we did not really want the list, we wanted to return false or true depending on whether the profile contained any of the tokens that should be displayed.

profiles.stream()
 .map(s->StringUtils.split(s.getToken(), ","))
 .flatMap(Arrays::stream)
 .distinct()
 .map(StringUtils::trim)
 .collect(Collectors.toList())
 .stream()
 .anyMatch(EnumToken::isShowInMenu);

To end we leave chapter 1, free download, of the documentation Java 8: (click here)