The Collection + array

Collection.stream()

Collection.parallelStream()

Arrays.stream(T array) or Stream.of()

  • The operation type of the flow
    • Intermediate: 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. These operations are lazy, meaning that they are called without actually starting the flow
    • Terminal: A stream can have only one Terminal operation. After this operation is performed, the stream is used as light and cannot be operated again. So this must be the last operation of the stream. The execution of the Terminal operation will actually start the stream traversal and will produce a result, or side effect.
    • Another operation is called short-circuiting.

Common operations

map

Map generates a 1:1 mapping, where each input element is converted to another element according to the rules

    List<Book> books = Lists.newArrayList();
    List<Integer> bookIds = books.stream()
                                .map(Book::getId) // From the Book object stream to the Integer stream
                                .collect(Collectors.toList());
Copy the code

flatMap

It is similar to map, except that each element is converted to a Stream object, which compresses the elements in the child Stream into the parent set. It is a one-to-many mapping

class Article {
    private List<String> tags;
}

articles.stream()
        .flatMap(article -> article.getTags().stream())
        .collect(Collectors.toSet());
Copy the code

filter

A test is performed on the original Stream, and the elements that pass the test are left to generate a new Stream that filters out elements that match the expression, rather than filtering out elements that match the expression

// Leave the even number
Integer[] sixNums = {1.2.3.4.5.6};
Integer[] evens = Stream.of(sixNums).filter(n -> n%2= =0).toArray(Integer[]::new);
Copy the code

forEach

The forEach method takes a Lambda expression and executes it on each element of the Stream

findFirst

Returns the first element of Stream, or null, with the return type: Optional findAny, Max /min, and reduce

reduce

The main function is to combine the Stream elements. It provides an initial value (seed) and then combines it with the first, second, and NTH elements of the preceding Stream according to the operator. In this sense, string concatenation, numeric sum, min, Max, and average are special Reduce.

BinaryOperator: Indicates the operation on two operands of the same type, producing the result of the same type as the operand. In the case where operands and results are of the same type.

// There is an initial value and the return value is not null
Integer sum = integers.reduce(0, (a, b) -> a+b);
Integer sum = integers.reduce(0, Integer::sum);
// There is no starting value, so the return is Optional
sumValue = Stream.of(1.2.3.4).reduce(Integer::sum).get();
Copy the code

collect

  • List<Book> to List<BookDTO>
List<BookDTO> bookDTOs = books.stream().map(this::convert2DTO).collect(Collectors.toList());

// Collectors. ToSet () then return Set
      

private BookDTO convert2DTO(Book book) {
    // convert Book to BookDTO
}
Copy the code
  • List<Book> to Map<Integer, Book>Into the Map
class Book {
    private Integer id;
    private String name;
}
Map<Integer, Book> bookMap = books.stream().collect(Collectors.toMap(Book::getId, Function.identity()));
Copy the code
  • List<Book> to Map<Integer, String>Convert to a Map of the book ID and title
class Book {
    private Integer id;
    private String name;
}
Map<Integer, String> bookIdNameMap = books.stream().collect(Collectors.toMap(Book::getId, Book::getName));
Copy the code
  • List<Book> to Map<Integer, List<Book>>Group by book
class Book {
    private Integer id;
    private String name;
    private Integer categoryId;
} 

Map<Integer, List<Book>> categoryBooksMap = books.stream().collect(Collectors.groupingBy(Book::getCategoryId));
Copy the code
  • List<Book> to Map<Integer, List<String>>Group by book category and convert the grouping results into book titles
class Book {
    private Integer id;
    private String name;
    private Integer categoryId;
} 

Map<Integer, List<String>> categoryBookNamesMap = books.stream().collect(Collectors.groupingBy(Book::getCategoryId, Collectors.mapping(Book::getName, Collectors.toList())));
Copy the code
  • List<Book> to Map<Integer, Integer>Group books by category and get the number of books in each category
class Book {
    private Integer id;
    private String name;
    private Integer categoryId;
}

Map<Integer, List<String>> categoryBookNamesMap = books.stream().collect(Collectors.groupingBy(Book::getCategoryId, Collectors.counting()));
Copy the code

About efficiency

There are plenty of streaming and iterative efficiency comparisons on the web, some high, some low, and some confusing. In fact, those experiments are problematic in design (for example, there is only one business operation). Understanding how flow works will help you understand why people say it will improve efficiency and why it will slow down.

We have A List

books with 10 books, and we want to perform three business operations A(), B(), and C() on each Book in sequence, assuming that each business operation takes 1 second.

  • In iterative mode, the first book completes A(), B(), and C() before the second book begins A(), B(), and C(). Therefore, the total time is 10*(1+1+1)=30 seconds

  • When using A stream, when the first book has finished A() and entered B(), the second book can enter A() for inspection, as shown in the picture! So the time of the stream is: 10 + 3 = 13 seconds

A blunt summary

Most of the development is done with object processing, so you can eat the flow without worrying about efficiency in 99% of the scenarios. At the same time, the flow of code/business expression is stronger, used well can make your code/business process pleasing to the eye. By the way, it can also help you develop the habit of taking out methods 🤦♂️