Lambda expression and StreamAPI in Java8

Java8 provides new features for functional programming. This article is a reading summary of "Java8 combat"

Parametric behavior

A software development model that takes a method as a value and passes it where parameters need to be passed to adapt to changing needs and reduce workload.

Implementation of behavior parameterization:

  1. Strategy mode, the code to be passed is wrapped in the object

  2. Anonymous class

  3. Lambda a
    concise way to express anonymous functions

    • Composition: parameters, arrows, body

    • Use:
      (1) Where: Lambda expressions can be applied where functional interface parameters or variable types are passed. In other words, you can directly assign the Lambda expression to the functional interface variable, or you can pass the Lambda expression with the functional interface as the formal parameter when calling the method.
      (2) What: functional interfaces refer to those interfaces that only define an abstract method (yes, after JDK8, you can not only include abstract methods, but also provide methods with default implementations, marked with default), functional formulas provided by JDK Interfaces are annotated with @FunctionalInterface, and it is also recommended to use this annotation when creating our own functional interfaces.
      (3) How: The functional interfaces provided by JDK can be divided into two categories: generic function interfaces and primitive type specialization. Primitive type specialization is specifically designed for primitive types in order to solve the performance problems caused by automatic unpacking of packaging types. .

    • Scenario: surround execution

    • Type check, inference:
      check: determine the target type according to the context
      Inference: where Lambda is used, the parameter part can be omitted to mark the parameter type similar to the following form The compiler can infer the type of the parameter based on the context.

      (a1, a2) -> a1. getWeight(). compareTo( a2. getWeight());

    • Use of local variables:
      Lambda expressions can use variables of outer objects. You need to follow the principle of de facto final variables. The reason is that Lambda expressions may be recycled when they directly access local variables allocated on the stack, so the access is The copy is also to limit the change of external variables, it will hinder parallel processing.

  4. Method refers
    to a syntactic sugar for Lambda expressions. When a Lambda just directly calls a method, it can be expressed by method reference.

Note: The strategy pattern is a well-known design pattern. In this pattern, we abstract the strategy interface, implement this interface to reach the strategy class, and pass specific strategy objects where the formal parameters of the strategy interface are accepted. The final implementation passes the code as a parameter to another method, which means that the execution of the method is delayed. This is perhaps the most easily understood design pattern.

One problem with the strategy pattern is that if you only need to pass a specific implementation strategy once, the defined implementation class is only instantiated once, and the code is more troublesome to write. At this time, we can define a class with no name through the anonymous class. Instantiated when the code is executed.

However, the anonymous class has the problems of tedious definition and unreadable code, so the Lambda expression method appears to implement the behavior parameterization mode. We can pass Lambda expressions where the formal parameters are functional interfaces . The typical expression is "parameter-> method body". In Java8, according to the different parameters and return values, many commonly used functional interfaces are built in. At the same time, in order to avoid the efficiency problem of unpacking the original type, a corresponding specialized version of the original type is provided. Of course, if necessary, you can also define your own Required functional interface.

Commonly used functional interfaces provided in Java 8:
Commonly used functional interfaces
Note that these interfaces also provide methods that can be compounded (implemented by default methods), used to implement multiple Lambda composite processing, and can build powerful and intuitive Lambda.

The method reference is further simplified on the basis of the Lambda expression. If the method body of a Lambda expression is just a method of calling the parameter object, this expression can be replaced by a method reference, such as Person p-> p .getName () This expression can be written as a method reference: Person :: getName, method references are divided into the following three categories:

  1. Static method call
    ClassName :: staticMethod
  2. Type instance method
    (args0, rest)-> args0.instanceMethod (rest) can be written as Args :: instanceMethod
  3. The method
    person :: getName of the object where the context already exists

summary

Strategy patterns, anonymous classes, Lambda expressions, and method references are all for the purpose of parameterizing behavior. In order to reduce wordiness and make the code more readable, there is nothing new in essence, but with these foundations, the application of StreamAPI becomes possible. Through StreamAPI, we can achieve the purpose of functional data processing, and the construction is very complicated and simple Declarative programming.

StreamAPI

The StreamAPI is provided in Java8, which allows us to connect multiple operations in series to form a pipeline, like a SQL statement-like way to complete specific operations declaratively. We can no longer care about the implementation details of internal iterative processing, and at the same time, the implemented code is more concise, easy to read, flexible (compositable), and has better performance.

Purpose: To solve two problems in collection processing

  • Routines and obscurity-internal iteration
  • Difficult to utilize multi-core-parallel processing

Compare to Collection

  • Collection is mainly for storing and accessing data
  • Stream is mainly used to describe the calculation of data

How to use

  • Build stream

    • Collection.stream() / .parallelStream()
  • Intermediate operation: connect multiple streams

    • filter
      • filter(predicate)
      • distinct
    • slice
      • limit
      • skip
    • Map
      • map
      • flatMap
  • Terminal operation

    • forEach
    • Find
      • findFirst
      • findAny
    • match
      • allMatch
      • firstMatch
      • anyMatch
      • noneMatch
    • Statute
      • reduce (T identity, BinaryOperator accumulator)
        combines the elements in the stream to get a value, which is called folding in functional programming terminology

      • collect(Collector)

        • GroupingBy
        • PartitioningBy

The intermediate operation generates a stream object, generally refers to an instance of the Stream class, but sometimes in order to avoid the unpacking problem of the original type, the stream needs to be converted into a stream of the original type specialization (IntStream / LongStream / DoubleStream), the Stream interface provides methods such as mapToInt , And box method to achieve the conversion of stream types.

Stream operations can also be classified according to statefulness. For example, filter and map operations are stateless, while reduce, max, sum and other operations require internal state to accumulate results, they are stateful, and some operations in stateful According to whether the required storage is bounded, it can be divided into bounded operation and unbounded operation. For example, sort and distinct are unbounded, while skip, limit, and reduce are bounded.

The collect method receives the Collector parameters to perform the specification operation. The Collectors utility class provides a lot of static factory methods to easily construct the Collector, including toList (), toSet (), joining (), summingInt (), etc., and these methods are In order to facilitate the application of developers, the essence can be expressed by reducing ().

The same is the protocol operation, the difference between reduce and collect is

The reduce method aims to combine two values ​​to generate a new value, which is an immutable reduction. In contrast, the design of the collect method is to change the container so as to accumulate the results to be output. The collect method is also more suitable for parallel operations.

note:

  1. The stream can only be traversed once
  2. Short-circuit characteristics
    limit, findAny, skip and other intermediate operations use short-circuit characteristics similar to logical AND or
  3. Some terminal operations return the result as an object of Option <> class

Common Stream operations are shown in the table below
Common Stream operations

summary

StreamAPI hides the details of internal iterations, making full use of Lambda expressions, so that we can process data collection declaratively, the intermediate operations in StreamAPI can build an operation pipeline, and terminal operations trigger the execution of the pipeline to obtain the final result. The protocol and grouping functions can realize the long-winded function coding conveniently and easily. Finally, on the basis of hiding the iterative details, StreamAPI helps us to achieve parallel operations more easily.

Published 159 original articles · praised 225 · 210,000 views

Guess you like

Origin blog.csdn.net/lyg673770712/article/details/84591967