Select a single element
Intuitively, selecting a single element is definitely easier than selecting multiple elements, but there are some problems here. Let's first look at what the problem is with the general approach, and then look at how to use lambda expressions to solve it.
Let's first create a new method to find an element starting with a specific letter, and then print it out.
Copy the code code as follows:
public static void pickName(
final List<String> names, final String startingLetter) {
String foundName = null;
for(String name : names) {
if(name.startsWith(startingLetter)) {
foundName = name;
break;
}
}
This method is as stinky as the garbage truck that just passed by. We first created a new foundName variable, and then initialized it to null - this is the source of the stench. We have to check if it is null, otherwise a NullPointerException or an error response will be thrown. We also use an external iterator to loop through the list. If we find the element we want, we have to break out of the loop, which adds to the original smell: basic type paranoia, imperative style, mutability, all come alive. . Once we exit the loop, we have to check the results before printing. Such a small task actually requires such a long code.
Let’s reanalyze this issue. We just want to be able to select the first matching element and safely handle the case where no such element exists. Let's rewrite this pickName method using a lambda expression.
Copy the code code as follows:
public static void pickName(
final List<String> names, final String startingLetter) {
final Optional<String> foundName =
names.stream()
.filter(name ->name.startsWith(startingLetter))
.findFirst();
System.out.println(String.format("A name starting with %s: %s",
startingLetter, foundName.orElse("No name found")));
}
Some powerful functions in the JDK make this code very concise. First, we use the filter method to obtain all elements that meet the conditions, and then use the findFirst method of the Stream class to select the first element of the returned collection. This method returns an Optional object, which is the officially certified deodorizer for null variables in Java.
The Optional class is very useful, you don't have to worry about whether the result exists or not. It saves us from the trouble of null pointer exceptions and makes it clearer that no result is a possible result. Through the isPresent() method, we can know whether the result exists. If we want to get the result value, we can use the get() method. We can also set a default value for it using (the name of this method will shock you) the orElse method, just like in the previous code.
We use the friends collection we have been using before to verify our pickName method.
Copy the code code as follows:
pickName(friends, "N");
pickName(friends, "Z");
This code selects the first matching element and prints a friendly message if it is not found.
Copy the code code as follows:
A name starting with N: Nate
A name starting with Z: No name found
The combination of the findFirst() method and the Optinal class reduces the amount of our code and looks good. But the functions of the Optional class are much more than that. For example, in addition to providing a default value when the object does not exist, you can also use it to run a piece of code, or a lambda expression, if the result exists, like this:
Copy the code code as follows:
foundName.ifPresent(name -> System.out.println("Hello " + name));
Compared with the imperative code of selecting the first matching name, the elegant functional style of the flow looks better. But is there too much to do in this version of the call flow? Of course not, these methods are very smart and they work on demand (we will explore this in depth in Lazy Evaluation of Streams on page 113).
The example of selecting a single element shows more powerful functions of the JDK library. Let's take a look at how lambda expressions can find a desired value based on a collection.