New to Java8 are Lambda expressions and streams. When used together, streams and Lambda expressions can make code concise and readable because of the declarative nature of streams that handle data sets

How can streams simplify code

If there is a requirement to perform a processing on the dishes found in the database:

  • Screen for dishes with fewer than 400 calories
  • Make a ranking of the selected dishes
  • Gets the name of the sorted dish

Cuisine: Dish. Java

public class Dish {
    private String name;
    private boolean vegetarian;
    private int calories;
    private Type type;
    // getter and setter
}
Copy the code

How Java8 was implemented before

private List<String> beforeJava7(List<Dish> dishList) { List<Dish> lowCaloricDishes = new ArrayList<>(); For (Dish Dish: Dish list) {if (Dish. Getcalorie () < 400) {lowCaloricDishes. Add (Dish); Sort (lowCaloricDishes, new Comparator<Dish>() {@override public int compare(Dish o1, Dish o2) { return Integer.compare(o1.getCalories(), o2.getCalories()); }}); List<String> lowCaloricDishesName = new ArrayList<>(); for (Dish d : lowCaloricDishes) { lowCaloricDishesName.add(d.getName()); } return lowCaloricDishesName; }Copy the code

Implementation after Java8

private List<String> afterJava8(List<Dish> dishList) { return dishList.stream() .filter(d -> d.getCalories() < 400) Sorted (comparing(Dish:: getcalorie)) // Sort by calories. Map (Dish::getName) // Extract the Dish name .collect(Collectors.toList()); // Convert to List}Copy the code

It’s easy to do things in five lines that used to take 24 lines of code

Now there is a new requirement, which is as follows:

Classify the dishes found in the database according to the types of dishes and return a result of Map<Type, List>

This would have been creepy before JDK8

How Java8 was implemented before

private static Map<Type, List<Dish>> beforeJdk8(List<Dish> dishList) {
    Map<Type, List<Dish>> result = new HashMap<>();

    for (Dish dish : dishList) {
        //不存在则初始化
        if (result.get(dish.getType())==null) {
            List<Dish> dishes = new ArrayList<>();
            dishes.add(dish);
            result.put(dish.getType(), dishes);
        } else {
            //存在则追加
            result.get(dish.getType()).add(dish);
        }
    }

    return result;
}
Copy the code

Fortunately, JDK8 has streams, so you don’t have to worry about complex collection handling requirements anymore

Java8 after the implementation

private static Map<Type, List<Dish>> afterJdk8(List<Dish> dishList) {
    return dishList.stream().collect(groupingBy(Dish::getType));
}
Copy the code

See the power of streams. We’ll cover streams in more detail

2 What is flow

A stream is a sequence of elements generated from a source that supports data processing operations. The sources can be arrays, files, collections, or functions. A stream is not a collection element, it is not a data structure, it does not hold data, and its primary purpose is computation

3 How do I generate streams

There are five main ways to generate streams

1. Collection generation is the most common one in applications

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream();
Copy the code

A stream is generated through the stream method of the collection

2. Generate an array

int[] intArr = new int[]{1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(intArr);
Copy the code

The arrays. stream method generates streams that are numeric streams rather than streams. The use of numerical flow can avoid unpacking during calculation and improve performance.

The Stream API provides mapToInt, mapToDouble, and mapToLong methods for converting a Stream into a numeric Stream. It also provides a boxed method for converting a Stream into an object Stream

3. Generate values

Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Copy the code

A Stream is generated by the of method of a Stream, and an empty Stream is generated by the empty method of a Stream

4. Generate the file

Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())
Copy the code

A stream is obtained using the files.line method, and each resulting stream is a row in a given file

5. Static methods for generating streams from a function are provided through function generation, iterate and generate

iterator

Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
Copy the code

The iterate method takes two arguments, the first as the initialization value and the second as the function operation. Because the iterator stream is infinite, it is truncated by the limit method, producing only five even numbers

generator

Stream<Double> stream = Stream.generate(Math::random).limit(5);
Copy the code

The Generate method takes a parameter of type Supplier, which provides the value for the stream. The flow generated by Generate is also infinite, so it is truncated by limit convection

4 Operation types of streams

There are two main types of operations for streams

1. Perform intermediate operations

A stream can be followed by zero or more intermediate operations. The main purpose is to open the stream, do some degree of data mapping/filtering, and then return a new stream to be used by the next operation. Such operations are inert, and the stream traversal does not really start when such methods are called. The real traversal needs to wait for terminal operations. Common intermediate operations include filter, map, etc., which will be introduced below

2. Terminal operations

A stream can only have one terminal operation. After this operation is performed, the stream is closed and cannot be operated again. Therefore, a stream can only be traversed once. It is the execution of the terminal operation that really begins the stream traversal. For example, count and collect are introduced below

5 flow using

The use of streams will be divided into terminal operations and intermediate operations

In the middle of operation

The filter screen

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);
Copy the code

Filter method is used to filter conditions. The parameter of filter method is a condition

Distinct Removes duplicate elements

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();
Copy the code

Use the DISTINCT method to quickly remove duplicate elements

Limit Returns the specified number of streams

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().limit(3);
Copy the code

The limit method specifies the number of streams to return. Limit must be >=0, otherwise an exception will be thrown

Skip Skips elements in the stream

List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().skip(2);
Copy the code

Skip the elements in the stream by skipping the first two elements in the example above, so it prints 2,3,4,5. Skip must be >=0, otherwise an exception will be thrown

The map flow mapping

A flow map is a mapping of an accepted element to another element

List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
Stream<Integer> stream = stringList.stream().map(String::length);
Copy the code

The mapping can be done using the map method. This example completes the mapping of String -> Integer. The previous example completed the mapping of Dish->String using the map method

FlatMap flow conversion

Converts every value in one stream to another

List<String> wordList = Arrays.asList("Hello", "World");
List<String> strList = wordList.stream()
        .map(w -> w.split(" "))
        .flatMap(Arrays::stream)
        .distinct()
        .collect(Collectors.toList());
Copy the code

Map (w -> w.split(” “)) returns Stream<String[]>. If we want to get a Stream, we can convert Stream ->Stream to flatMap

Match elements

Three matching methods are provided

1. AllMatch matches all matches

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); If (integerlist.stream ().allmatch (I -> I > 3)) {system.out.println (" all values greater than 3"); }Copy the code

Through allMatch method

2. AnyMatch matches one of them

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); If (integerlist.stream ().anymatch (I -> I > 3)) {system.out.println (" there is a value greater than 3 "); }Copy the code

Is equivalent to

For (Integer I: integerList) {if (I > 3) {system.out.println (" there is a value greater than 3 "); break; }}Copy the code

Prints if there is a value greater than 3, which is implemented in java8 through the anyMatch method

3. NoneMatch all don’t match

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5); If (integerlist.stream ().nonematch (I -> I > 3)) {system.out.println (" values less than 3"); }Copy the code

Through the noneMatch method

6 Terminal Operations

Count the number of elements in the flow

1. Through the count

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();
Copy the code

Count the number of elements in the stream by using the count method

2. By counting

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().collect(counting());
Copy the code

This last method of counting the number of elements is particularly useful when used in conjunction with COLLECT

To find the

There are two ways to find it

1. FindFirst

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();
Copy the code

Find the first element greater than three using the findFirst method and print it

2. FindAny randomly

List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();
Copy the code

The findAny method finds one element greater than three and prints it. For internal optimization reasons, it ends when it finds the first element greater than three, which gives the same result as the findFirst method. The findAny method is provided to make better use of parallel streams, whereas the findFirst method has more limitations on parallelism

Reduce groups elements in the flow

Suppose we sum the values of a set

Jdk8 before

int sum = 0;
for (int i : integerList) {
sum += i;
}
Copy the code

Jdk8 is processed by reduce

int sum = integerList.stream().reduce(0, (a, b) -> (a + b));
Copy the code

This can be done in a single line, or you can use the method reference shorthand:

int sum = integerList.stream().reduce(0, Integer::sum);
Copy the code

Reduce takes two arguments, an initial value of 0 in this case, and a BinaryOperator Accumulator to combine the two elements to produce a new value,

The Reduce method also has an overloaded method with no initialization value

Gets the minimum maximum value in the stream

Obtain the minimum and maximum value by min/ Max

Optional<Integer> min = menu.stream().map(Dish::getCalories).min(Integer::compareTo);
Optional<Integer> max = menu.stream().map(Dish::getCalories).max(Integer::compareTo);
Copy the code

It can also be written as:

OptionalInt min = menu.stream().mapToInt(Dish::getCalories).min();
OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max();
Copy the code

Min gets the minimum value in the stream and Max gets the maximum value in the stream using the Comparator<? super T> comparator

Get the minimum and maximum values by minBy/maxBy

Optional<Integer> min = menu.stream().map(Dish::getCalories).collect(minBy(Integer::compareTo));
Optional<Integer> max = menu.stream().map(Dish::getCalories).collect(maxBy(Integer::compareTo));
Copy the code

MinBy gets the minimum value in the stream and maxBy gets the maximum value in the stream using the Comparator<? super T> comparator

Obtain the minimum and maximum value through reduce

Optional<Integer> min = menu.stream().map(Dish::getCalories).reduce(Integer::min);
Optional<Integer> max = menu.stream().map(Dish::getCalories).reduce(Integer::max);
Copy the code

7 summary

By using the Stream API, you can simplify your code and improve the readability of your code