In Java 8, thanks to the functional programming enabled by lambda, a new Stream concept was introduced to address the existing drawbacks of collections.

About the use of ::

  1. : :Is used to assign implementation methods to single-method interfaces
  2. The implementation method to be used for appointment needs to be static or constructor
  3. The constructor is equivalent to the class that returns the constructor

The sample

Interface class to be assigned:

@FunctionalInterface
public interface ITest<T> {

    T test(String s);

}
Copy the code

Classes used to implement interfaces:

public class SomeThingTest {
    SomeThingTest(String s) {

        System.out.println("Constructor implementation");
    }

    static String staticTest(String s) {
        System.out.println("Static method implementation");
        return "Static method"; }}Copy the code

Use:

public class MyMain {
    public static void main(String[] args) {

        ITest iTest = SomeThingTest::new;
        ITest iTest2 = SomeThingTest::staticTest;
        iTest.test("a");
        iTest2.test("a"); }}Copy the code

About lambda expressions

Two usages:

Substitution function interface
  1. Instead of functional interfaces. A functional interface is simply an interface that contains only an abstract method, such as java.lang.Runnable and java.util.Com Parator in the Java standard library are typical functional interfaces. For functional interfaces, in addition to the standard Java methods for creating implementation objects, lambda expressions can be used to create implementation objects, which greatly simplifies code implementation. When lambda expressions are used, only formal parameters and method bodies need to be supplied. Since a functional interface has only one abstract method, the method body declared by a lambda expression must be the implementation of that unique abstract method, and the types of formal parameters can be automatically inferred from the method’s type declaration (that is, formal parameters can omit types).

First we need to know the properties that the implementation class needs to know to implement the single-method interface:

1. The method requires the value passed in

2. The value returned by the method

Both of these are expressed in lambda expressions:

(value passed by method) -> Value returned by method

Void (); void (); void ();

(value passed by method) -> Some operations within the method

Example:
@FunctionalInterface
public interface ITest<T> {

    void test(String s);

}
Copy the code
ITest<String> stringITest = s -> System.out.println(s);
Copy the code
Works with collections
  1. Works with collections. Java 8 has two new packages for batch manipulation of collection data: java.util.function and java.util.stream. Arguably, lambda expressions and Streams are the biggest changes to the Java language since generics and annotations were added, and lambda expressions have greatly influenced the way we code when dealing with collections. This will be reflected below

about<? extends T>and<? super T>

  1. <? extends T>Represents match genericsTAnd its subclasses
  2. <? super T>Represents match genericsTAnd its parent class

Stream flow operation

Original collection – > Stream – > various operations (filtering, grouping, statistics) – > Terminal operations

The operation of a Stream is usually done by converting a collection to a Stream, and then by various operations such as filtering, filtering, grouping, and calculation. The final terminal operation is to convert it into the data we want. The form of this data is generally a set, and sometimes it will output count as required.

features

  1. No storage: A Stream is a data-source-based object that does not store data elements itself, but pipes the data source’s elements to an operation.
  2. Functional programming: Any modification to a Stream does not modify the data source behind it. For example, a filter operation on a Stream does not remove the filtered element, but rather produces a new Stream without the filtered element.
  3. Delayed execution: The operation of a Stream consists of zero or more intermediate operations and one terminal operation. The intermediate operations defined by a Stream are executed sequentially only if the end operation is performed, which is the delayed nature of the Stream.
  4. Consumability: A Stream can only be “consumed” once and is invalidated once traversed. Like a container iterator, to iterate again you must regenerate a new Stream.

Create a flow

Stream () : serial stream

ParallelStream () : Parallel streams

When creating a Stream, the default is to create a serial Stream. But you can use parallelStream() to create parallel streams or parallel() to replace serial streams with parallel streams. Parallel streams can also be converted to serial streams by sequential().

Filter (T-> Boolean)

The filter -> arrow is followed by a Boolean value, which can be used to write any filter criteria. In other words, it can be used to write anything that can be implemented in SQL

Personal test understanding, for reference only

When it comes to the use of this method, I have doubts. It is not difficult to simply understand its usage, but what is the sequence of its specific implementation? Enter the source code of filter, you can see:

Stream<T> filter(Predicate<? super T> predicate);
Copy the code
public interface Predicate<T> {
    boolean test(T t);
    }
Copy the code

It’s obvious that lambda expressions implement the test method in Predicate. The return value is Boolean, and the input parameter is T, but because <? Super T>, which means that all objects stored in the set are the parent class of T, so the parameter T entered here must be T or a subclass of T. However, after implementation, the filter method does not directly call the test method (for convenience, I did not use lambda expressions).

        List<String> names = new ArrayList<>();
        names.add("Zhang Sanfeng");
        names.add("Zhang Dabao");
        names.add("Zhang");
        names.add(demarger);
        names.add("Xiao feng.");
        names.add("Li Dabao");

       names.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                System.out.println(s);
                return s.startsWith("Zhang"); }}); System.out.println("The end");
Copy the code

Results:

The test method was executed after the collect method was called

        List<String> names = new ArrayList<>();
        names.add("Zhang Sanfeng");
        names.add("Zhang Dabao");
        names.add("Zhang");
        names.add(demarger);
        names.add("Xiao feng.");
        names.add("Li Dabao");

        List<String> z = names.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                System.out.println(s);
                return s.startsWith("Zhang");
            }
        }).collect(Collectors.toList());
        System.out.println(z.toString());
        System.out.println("The end");
Copy the code

Results:

Once the filter call is implemented, the test method in the Predicate is not executed directly. Instead, it waits for other methods in the stream that can call the object. @ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $@ $

Distinct to heavy

This is similar to the DISTINCT keyword in SQL.

/ / distinct to heavy
List<User> distinctList = filterList.stream().distinct()
        .collect(toList());
Copy the code

Sorted order

If the classes of the elements in the stream implement the Comparable interface, that is, they have their own sorting rules, then the sorted() method can be called directly to sort the elements. The no-parameter method requires that the elements in the stream implement the Comparable interface. Or you will quote Java. Lang. ClassCastException, have reference method needs to call sorted ((T, T) – > int) realize the Comparator interface

//sorted()
List<User> sortedList = distinctList.stream().sorted(Comparator.comparingInt(User::getAge))
        .collect(toList());
Copy the code

Limit (long n) returns the element

Returns the first n elements

//limit returns the first n elements
List<User> limitList = sortedList.stream().limit(1)
        .collect(toList());
Copy the code

Skip (long n) Skips elements

As opposed to limit, skip means to skip, to remove the first n elements.

Map (T -> R) type conversion

Map converts T data into R data:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Copy the code

Let’s say we have a set of students and we need to extract the ages of the students to analyze the age distribution curve of the students. Prior to Java 8 we would consume the age attribute in the element by creating a new collection and then iterating through the student collection. We have now fulfilled this requirement with a very simple streaming operation.

 / / pseudo code
 List<Integer> ages=studentList.stream().map(Student::getAge).collect(Collectors.toList());
Copy the code

FlatMap (T -> Stream) indicates a Stream

Convert a class to a stream type:

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Copy the code

The ages of all students under all classes were extracted to analyze the age distribution curve of students. If using map to get student stream:

 List<List<Student>> studentGroup= gradeList.stream().map(Grade::getStudents).collect(Collectors.toList());
Copy the code

If you find that List<List> is nested, you also need to pull it out one by one and convert it to a stream using flatMap:

 // flatMap extract List
      
        map extract age
      
 List<Integer> ages = grades.stream().flatMap(grade -> grade.getStudents().stream()).map(Student::getAge).collect(Collectors.toList());
Copy the code

AllMatch (T-> Boolean) Checks for all matches and returns Boolean

Check whether all the parameter behaviors are met. If these users are the list of Internet cafe users, it is necessary to check whether everyone is 18 years old or not.

boolean isAdult = list.stream().allMatch(user -> user.getAge() >= 18);
Copy the code

AnyMatch (T-> Boolean) Checks if there are any matching objects and returns Boolean

Check if any element satisfies a given condition, for example, if you want to know if there are girls in the class list.

//anyMatch(T -> Boolean) Specifies whether any element satisfies the given condition
boolean isGirl = list.stream().anyMatch(user -> user.getSex() == 1);
Copy the code

NoneMatch (T -> Boolean) Check whether all matches

Like checking to see if there are no users from Paris.

boolean isLSJ = list.stream().noneMatch(user -> user.getAddress().contains("Paris"));
Copy the code

FindFirst (): Finds the first element

Optional<User> fristUser  = list.stream().findFirst();
Copy the code

FindAny (): Finds any element

Optional<User> anyUser  = list.stream().findAny();
Copy the code

Here we see that findAny always returns the first element as well, so why make the distinction? Because the parallelStream parallelStream() does find any element.

Max and min Maximum and minimum values

Property property = properties.stream()
            .max(Comparator.comparingInt(p -> p.priceLevel))
            .get();
Copy the code

When looking for the largest or smallest element in a Stream, the first thing to consider is what sort indicator to use. Take finding the shop with the lowest price as an example, the sorting index is the price grade of the shop. To order the Stream object by price rank, pass it a Comparator object. Java8 provides a new static method, comparingInt, that makes it easy to implement a comparator. In the past, we needed to compare the value of an attribute between two objects. Now we only need to provide an access method.

Collect Result

Get the 2 stores closest to me:

List<Property> properties = properties.stream()
            .sorted(Comparator.comparingInt(x -> x.distance))
            .limit(2)
            .collect(Collectors.toList());
Copy the code

Get the price level of each store:

Map<String, Integer> map = properties.stream()
        .collect(Collectors.toMap(Property::getName, Property::getPriceLevel));
Copy the code

A list of stores in all price levels

Map<Integer, List<Property>> priceMap = properties.stream()
                .collect(Collectors.groupingBy(Property::getPriceLevel));
Copy the code

Peek looks at elements of a step

The PEEK method can consume each element without adjusting the order or number of elements, and then generate a new stream if you want to see how each element flows through multiple stream operations

Stream.of("one"."two"."three"."four")
     .filter(e -> e.length() > 3)
     .peek(e -> System.out.println("Filtered value: " + e))
     .map(String::toUpperCase)
     .peek(e -> System.out.println("Mapped value: "+ e)) .collect(Collectors.toList()); Filtered value: three Mapped Value: three Mapped Value: four Mapped value: fourCopy the code

The forEach traversal

The forEach method works like a regular for loop, except that it supports multithreaded traversal, but not in order

        List<String> names = new ArrayList<>();

        Stream.of("one"."two"."three"."four")
            .filter(e -> e.length() > 2)
            .map(String::toUpperCase)
            .forEach(s -> names.add(s));
        System.out.println(names.toString());
Copy the code

ForEachOrdered traversal

The forEachOrdered method can be used to guarantee sequential traversal, such as if the stream was passed in from outside and parallel was called before that to enable multi-threaded execution

Stream<String> stringStream = Stream.of("- 2"."1"."0"."1"."2"."3");
// Iterate over the output elements sequentially
stringStream.forEachOrdered(System.out::println);
// Multithreading through the output element, the following line is the same as the result of the above execution
//stringStream.parallel().forEachOrdered(System.out::println);
Copy the code

Elements in a toArray stream are converted to arrays

ToArray has one parameterless method and one parameterless method, which is used to convert elements in the stream into an Object array

Stream<String> stringStream = Stream.of("- 2"."1"."0"."1"."2"."3");
Object[] objArray = stringStream.toArray();
Copy the code

The parameter method toArray(IntFunction

Generator) supports converting elements in A stream to an array of elements of A specified type
[]>

Stream<String> stringStream = Stream.of("- 2"."1"."0"."1"."2"."3");
String[] strArray = stringStream.toArray(String[]::new);
Copy the code

Count Indicates the number of elements in the statistics flow

The count method is used to count the total number of elements in the stream

Stream<Integer> numStream = Stream.of(-2, -1.0.1.2.3);
//count=6
long count = numStream.count();
Copy the code

Optional class

Conclusion:

  1. OptionalClasses were created to solve problems common in programs with functional expressionsNullPointerException(null pointer exception) Exception
  2. When you’re pretty sure that an object can’tnullShould be usedof()Methods, otherwise, use whenever possibleofNullable()methods
  3. Use its map function expressions as much as possible to simplify if-else or null operations

basic

The java.util.Optional

class is a container object that encapsulates an Optional value. The Optional value can be null, and isPresent() returns true if the value exists.

Create an Optional object

The Optional class provides three methods to instantiate an Optional object: empty(), of(), and ofNullable(). These methods are static and can be called directly.

Its constructor is private and cannot be instantiated with new

 private Optional(a) {
        this.value = null;
    }
Copy the code

empty()Create an Optional object with no value:

 Optional<String> optionalITest=Optional.empty();
Copy the code

If calling isPresent() on a variable returns false, calling get() throws a NullPointerException.

of()Method creates an Optional object with a non-empty value:

String str = "Hello World";
Optional<String> notNullOpt = Optional.of(str);
Copy the code

If a null value is passed in, a NullPointerException is thrown

ofNullable()The method receives a value that can be null:

Optional<String> nullableOpt = Optional.ofNullable(str);
Copy the code

If STR is null, the resulting nullableOpt object is an Optional object with no value.

map(T -> R)Gets the set of values for the object in Optional

Optional<User> userOpt = Optional.ofNullable(user);
Optional<String> roleIdOpt = userOpt.map(User::getRoleId);
Copy the code

orElse(T other)Get a value, return if there is one, and give a default value of the same type if there is none

Optional<String> optionalITest=Optional.ofNullable(null);
String s = optionalITest.orElse("");
Copy the code

orElseGet(Supplier<? extends T>): Works like the orElse() method, except for how the default values are generated.

This method accepts a Supplier
a functional interface parameter used to generate default values;

Optional<List<String>> optionalITest=Optional.ofNullable(null);
optionalITest.orElseGet(()->new ArrayList<>());
Copy the code

orElseThrow(Supplier<? extends X> exceptionSupplier)Customizing the value method for throwing an exception.

An exception is also thrown when the value is null, but you can define your own exception

        Optional<List<String>> optionalITest=Optional.ofNullable(null);
        optionalITest.orElseThrow(() -> new IllegalArgumentException("Self-imposed exception"));
Copy the code

ifPresent(Consumer<? super T>)Method receivesConsumer<? super T>Generally used for printing to the background

Optional<String> strOpt = Optional.of("Hello World");
strOpt.ifPresent(System.out::println);
Copy the code

filter(Predicate<? super T> predicate)filter

With the Stream filter (Predicate
predicate)

Stream returns a filtered stream object. Optional returns a filtered optional object

Optional<String> optionalITest=Optional.ofNullable("add");
        Optional<String> a = optionalITest.filter(s -> s.startsWith("a"));
        a.ifPresent(System.out::println);
Copy the code

orElse()Use of methods

Simplify:

Original:

returnstr ! =null ? str : "Hello World"
Copy the code

After the improvement:

return strOpt.orElse("Hello World")
Copy the code

To simplify the if – else

Simplify:

Original:

User user = ...
if(user ! =null) {
    String userName = user.getUserName();
    if(userName ! =null) {
        return userName.toUpperCase();
    } else {
        return null; }}else {
    return null;
}
Copy the code

After the improvement:

User user = ...
Optional<User> userOpt = Optional.ofNullable(user);

return userOpt.map(User::getUserName)
            .map(String::toUpperCase)
            .orElse(null);
Copy the code

Reference article: juejin.cn/post/684490… Juejin. Cn/post / 684490… Juejin. Cn/post / 684490… Juejin. Cn/post / 684490… Juejin. Cn/post / 684490… Juejin. Cn/post / 684668…