Here are some study notes from the blogger studying java8 in action.

Starting with this section, you’ll move on to Learning about Java8 Streams.

Some examples in this article are based on the following scenarios: restaurant order, Dish refers to the dishes available in the restaurant, Dish is defined as follows:

Public class Dish {/** Dish name */ private final String name; */ private final Boolean vegetarian; /** calorie */ private final int calories; /** Type */ private final Type Type; public Dish(String name, boolean vegetarian, int calories, Type type) { this.name = name; this.vegetarian = vegetarian; this.calories = calories; this.type = type; } public enum Type {MEAT, FISH, OTHER}Copy the code

The data of the menu is as follows:

List<Dish> menu = Arrays.asList(
new Dish("pork", false, 800, Dish.Type.MEAT),
new Dish("beef", false, 700, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER),
new Dish("rice", true, 350, Dish.Type.OTHER),
new Dish("season fruit", true, 120, Dish.Type.OTHER),
new Dish("pizza", true, 550, Dish.Type.OTHER),
new Dish("prawns", false, 300, Dish.Type.FISH),
new Dish("salmon", false, 450, Dish.Type.FISH) );
Copy the code

Let’s start the flow with a simple example: Find a vegetarian dish from a menu list and print the name of the dish.

Prior to Java8, we typically implemented this requirement like this:

List<String> dishNames = new ArrayList<>(); for(Dish d menu) { if(d.isVegetarian()) { dishNames.add(d.getName()); }} for(String n: dishNames) {system.out.println (n); }Copy the code

So in java8, we could write:

menu.streams() .filter( Dish::isVegetarian).map( Dish::getName) .forEach( a -> System.out.println(a) );
Copy the code

The result of its run:



How, magical!!

Before explaining the above code, let’s do a theoretical introduction to convection.

1. What is flow?

Streams are data streams and sequences of elements. In Java8, the stream interface is defined in the java.util.stream. stream package, and a new method is added to the Collection interface:

default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
}
Copy the code

A short definition of a stream: a sequence of elements generated from a source that supports data processing operations. For example, collections and arrays are data structures (containers) that support data operations and can be used as creation sources for streams. The core elements of this definition are as follows:

  • A source stream is created from a source that supports data processing, such as collections, arrays, and so on.
  • The element sequence flow represents a sequence of elements (pipeline) because it is created from a data processing source.
  • The flow of data processing operations focuses not on data storage but on data processing, such as filter, Map, forEach, and so on in the example.
  • Iteration A stream iterates internally, while a collection iterates externally. For example, we iterate through the Collection interface and require the user to do iteration, such as for-each, and then write the corresponding processing code in the loop body, which is called external iteration. In contrast, the Stream library uses internal iteration, where we simply pass in the corresponding function to indicate what to do.

Note: Streams, like the Iterator, can only be iterated once. To iterate more than once, create multiple streams.

Next, we will focus on the common operations of streams.

2. Common flow operation methods

2.1 filter The method of the filter function is declared as follows:

java.util.stream.Stream#filter
Stream<T> filter(Predicate<? super T> predicate);
Copy the code

The method receives a predicate that returns a stream, that is, the lambda expression that the filter method receives needs to satisfy (T -> Boolean).

Example: Select all vegetarian dishes from the menu:

List<Dish> vegetarianDishs = menu.stream(). Filter (Dish::isVegetarian) // Use filter to filter the dishes in the stream. Collect (toList ()); // Change the flow to a List, as described below.Copy the code

Tips: Flow operations can be divided into middleware operations and terminal operations. The intermediate operation usually returns a stream and will not be called immediately before the terminal operation is called. The intermediate operation will actually trigger execution only after the terminal method is called. The collect method in this example is the terminal method.

Let’s take a look at database query operations. In addition to the basic filtering actions, there are also deduplication, paging, etc. Does java8’s streaming API support these operations? The answer, of course, is yes.

2.1.1 distinct

Distinct, similar to the database in the weight function, is the result set deduplication. For example, if you have a number numArr = [1,5,8,6,5,2,6] and want to print all the odd numbers in the number and not print them again, how do you do that?

Arrays.stream(numArr).filter(  a -> a % 2 == 0 ).distinict().forEach(System.out::println);
Copy the code

2.1.2 limit

Truncate the stream, returning a stream whose I does not exceed the specified number of elements. If the number of elements to be printed is even, cannot be repeated, and only one element is printed, what is the implementation of the above example?

Arrays.stream(numArr).filter(  a -> a % 2 == 0 ).distinict().limit(1).forEach(System.out::println);
Copy the code

2.1.3 Skip skips the specified element and returns a stream of remaining elements, complementary to limit.

2.2 Map is similar to database operations. We can usually select only one column in a table, and java8 stream operations provide a similar method. For example, we need to extract the names of all the dishes from the menu. In java8 we can do this using the following code:

Version 1: List<String> dishNames = menu.stream().map((Dish D) -> d.getName()).collect(Collectors. ToList ()); Version 2: List<String> dishNames = menu.stream().map(d -> d.getName()).collect(Collectors. ToList ()); Version 3: List<String> dishNames = menu.stream().map(Dish::getName).collect(Collectors. ToList ());Copy the code

The rest of the article uses the briefest lambda expressions possible.

Let’s look at the Stream declaration about the map method:

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

Accepts a Function declared T -> R, receives an object of type T, and returns an object of type R.

Of course, Java also defines the following methods to efficiently handle the underlying data types (without the performance cost of boxing and unboxing) :

IntStream mapToInt(ToIntFunction<? super T> mapper)
LongStream mapToLong(ToLongFunction<? super T> mapper)
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper)
Copy the code

Question to consider: For character values [” Hello “, “World”], print the character sequence and de-duplicate it. First attempt:

public static void test_flat_map() {
    String[] strArr = new String[] {"hello", "world"};
    List<String> strList = Arrays.asList(strArr);
    strList.stream().map( s -> s.split(""))
                    .distinct().forEach(System.out::println);
}
Copy the code

Output result:



Why are two String[] elements returned? Stream<String[]> map(s -> s.split()) returns Stream<String[]>, Steam[String[]] returns Stream<String[]>

public static void test_flat_map() {
    String[] strArr = new String[] {"hello", "world"};
    List<String> strList = Arrays.asList(strArr);
    strList.stream().map( s -> s.split(""))
                    .map(Arrays::stream)
                    .distinct().forEach(System.out::println);
}
Copy the code

The return result is:



The result of map(Stream) is stream < stream < String>>, which contains two elements, each of which is a character stream. This can be verified by the following code:

public static void test_flat_map() { String[] strArr = new String[] {"hello", "world"}; List<String> strList = Arrays.asList(strArr); strList.stream().map( s -> s.split("")) .map(Arrays::stream) .forEach( (Stream<String> s) -> { System.out.println("\n --start---"); s.forEach(a -> System.out.print(a + " ")); System.out.println("\n --end---"); }); }Copy the code

After the map, two separate streams are returned. How can I merge the two streams into one stream and then disinic the stream?

The answer, of course, is yes, as the flatMap method makes its debut:

public static void test_flat_map() {
    String[] strArr = new String[] {"hello", "world"};
    List<String> strList = Arrays.asList(strArr);
    strList.stream().map( s -> s.split(""))
                    .flatMap(Arrays::stream)
                    .distinct().forEach( a -> System.out.print(a +" "));
}
Copy the code

Its output results:



As expected. In a nutshell, flatMap can merge two streams into a single stream for operation.

The Stream API provides allMatch, anyMatch, noneMatch, findFirst, and findAny methods to match and find data in the Stream.

2.3.1 allMatch Let’s first look at the declaration of this method:

boolean allMatch(Predicate<? super T> predicate);
Copy the code

Receives a Predicate function (T-> Boolean) that returns a Boolean value, which is a terminal operation that determines whether all elements in the stream match the Predicate. As long as one of the elements is not compound, this expression will return false. Here is an example: for example, there is a List a where the elements are 1,2,4,6,8. Determines whether the elements in the stream are all even.

Boolean result = a.stam (). AllMatch (a -> a % 2 == 0); // Will return false.Copy the code

2.3.2 anyMatch Function declaration of this method is as follows:

boolean anyMatch(Predicate<? super T> predicate)
Copy the code

Also receives a Predicate(T -> Boolean), which returns true as long as at least one of the elements in the stream matches the Predicate.

Here is an example: for example, there is a List a where the elements are 1,2,4,6,8. Determines whether elements in the stream contain even numbers.

Boolean result = a.stam ().anymatch (a -> a % 2 == 0); Boolean result = a.stam ().anymatch (a -> a % 2 == 0); // Will return true.Copy the code

2.3.3 noneMatch The function declaration of this method is as follows:

boolean noneMatch(Predicate<? super T> predicate);
Copy the code

Also receives a Predicate(T -> Boolean), which returns true as long as none of the elements in the stream match the Predicate expression.

Example: for example, there is a List a where the elements are 2,4,6,8. Check that none of the elements in the stream is odd.

Boolean result = a.stam ().nonematch (a -> a % 2 == 1); // Will return true.Copy the code

2.3.4 findFirst Finds an element in the stream with the following function declaration:

Optional<T> findFirst();
Copy the code

Returns an element in the stream. The return value is Optional. This is a class introduced in JDK8, commonly known as the value container class. The main purpose of this class is to avoid null Pointers and to handle null in a more elegant way. The detailed use of this class will be covered in the next article.

public static void test_find_first(List<Dish> menu) { Optional<Dish> dish = menu.stream().findFirst(); Dish. ifPresent(a -> system.out.println (a.get_name ()))); }Copy the code

2.3.5 findAny returns any element in the stream with the following function declaration:

Optional<T> findAny();
Copy the code

2.4 Reduce Reduction is sensitive to big data. The current Java8 flow operation is not a bit map-Reduce. Reduction refers to statistical analysis of all elements in the flow and reduction to a value. First, let’s take a look at the reduce function description:

T reduce(T identity, BinaryOperator<T> accumulator)
Copy the code
  • T identity: the initial value of the accumulator.
  • BinaryOperator< T> Accumulator: an accumulator function. BinaryOperator< T> extend BiFunction

    . A functional representation of a BinaryOperator that takes two input arguments of type T and returns a value of type T.
    ,>
Optional<T> reduce(BinaryOperator<T> accumulator);
Copy the code

If the stream is empty, it will return null, so the return value uses the Optional class to gracefully handle null values.

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
Copy the code

First, the final return value is of type U.

  • U identity: the initial value of the cumulative function.
  • BiFunction

    accumulator: this accumulator function is used to perform an accumulator for elements in the flow. Accumulator. Apply (identity, the type of the second parameter is not limited) only returns U.
    ,>
  • BinaryOperator< U> combiner: combiner. The results of the accumulator are combined. Because of reduced Reduce, the fork-join framework is used in Java flow calculation, and the elements in the stream are accumulated in parallel. Each thread processes part of the data in the stream, and finally the results are combined to obtain the final value.

Tip: One of the most important aspects of learning stream apis is to master these functional programming interfaces and then learn how to parameterize behavior using Lambda expressions (Lambda expressions are passed into functions as arguments).

Let’s show an example of how to use Reduce. Example 1: Summing elements in a collection

List<Integer> goodsNumber = Arrays.asList( 3, 5, 8, 4, 2, 13 ); Int sum = 0; for(Integer i : goodsNumber) { sum += i; // sum = sum + i; } System.out.println("sum:" + sum);Copy the code

The summation operator c = a + b, which takes two arguments and returns a single value of the same type.

Therefore, we can use T Reduce (T identity, BinaryOperator< T> Accumulator) to fulfill our requirements:

public static void test_reduce() { List<Integer> goodsNumber = Arrays.asList( 3, 5, 8, 4, 2, 13 ); int sum = goodsNumber.stream().reduce(0, (a,b) -> a + b); // int sum = goodsnumber.stream ().reduce(0, Integer::sum); System.out.println(sum); }Copy the code

The first parameter is the initial value T identity, and the second parameter is the element in the stream.

In what scenarios is the three-parameter Reduce function mainly used? Next, the summation example is used to show the usage scenario. In the Java multithreaded programming model, the fork-Join framework is introduced, which is to disassemble a large task, execute it in parallel with multiple threads, and finally merge it in pairs to get the final result. The third function of the reduce function is to combine this action. The following is an example of streaming processing that executes in parallel:

Public static void test_reduce_combiner() {/** Initialize the flow */ List<Integer> nums = new ArrayList<>(); int s = 0; for(int i = 0; i < 200; i ++) { nums.add(i); s = s + i; } // parallelStream (parallelStream) {// parallelStream (parallelStream) {// parallelStream (parallelStream) {// parallelStream (parallelStream) {// parallelStream (parallelStream) {// parallelStream (parallelStream); int sum2 = nums.parallelStream().reduce(0,Integer::sum, Integer::sum); System.out.println(" and are: "+ sum2); // The debug version of the above version is shown below. AtomicInteger accumulatorCount = new AtomicInteger(0); AtomicInteger combinerCount = new AtomicInteger(0); int sum = nums.parallelStream().reduce(0,(a,b) -> { accumulatorCount.incrementAndGet(); return a + b; }, (c,d) -> { combinerCount.incrementAndGet(); return c+d; }); System.out.println("accumulatorCount:" + accumulatorCount.get()); System.out.println("combinerCountCount:" + combinerCount.get()); }Copy the code

As you can see from the results, 100 cumulative actions were performed, but only 15 merges were performed.

The basic operation of stream is introduced here, and the current flow operation is summarized here: 1. Filter

  • Function Function: filter
  • Operation type: Intermediate operation
  • Return type: Stream
  • Functional interfaces: Predicate
  • Function descriptors: T -> Boolean

2, distinct,

  • Function Function: remove the weight
  • Operation type: Intermediate operation
  • Return type: Stream

3, skip

  • Skip () function Skip n elements
  • Operation type: Intermediate operation
  • Return type: Stream
  • Accept the argument: long

4, limit

  • The truncate () function returns the first n elements of a stream
  • Operation type: Intermediate operation
  • Return type: Stream
  • Accept the argument: long

5, the map

  • Function Function: mapping
  • Operation type: Intermediate operation
  • Return type: Stream
  • Function
    ,r>
  • Function descriptors: T -> R 6, flatMap
  • The flatten flow function merges multiple streams into a single stream
  • Operation type: Intermediate operation
  • Return type: Stream
  • Function
    ,>
  • Function descriptors: T -> Stream 7, sorted
  • Sort function
  • Operation type: Intermediate operation
  • Return type: Stream
  • Functional interface: Comparator
  • Function descriptor :(T,T) -> int 8, anyMatch
  • The match () function returns true if any matches in a stream
  • Operation type: Terminal operation
  • Return type: Boolean
  • Functional interfaces: Predicate
  • Function descriptors: T -> Boolean
  • The true () function returns true if all elements in a stream match
  • Operation type: Terminal operation
  • Return type: Boolean
  • Functional interfaces: Predicate
  • T -> Boolean 10, noneMatch
  • The stream () function returns true if all elements in a stream do not match
  • Operation type: Terminal operation
  • Return type: Boolean
  • Functional interfaces: Predicate
  • T -> Boolean 11, findAny
  • The stream () function returns an element from a stream
  • Operation type: Terminal operation
  • Return types: Optional 12, findFirst
  • The first element in a stream is returned
  • Operation type: Terminal operation
  • Return types: Optional 13, forEach
  • Function Function: traverses a stream
  • Operation type: Terminal operation
  • Return type: void
  • Functional interface: Consumer
  • Function descriptors: T -> collect
  • The stream conversion function converts a stream
  • Operation type: Terminal operation
  • Return type: R
  • Functional interface: Collector
    ,a,r>

15, reduce

  • Function function: protocol flow
  • Operation type: Terminal operation
  • Return type: Optional
  • Functional interface: BinaryOperator
  • Function descriptor :(T,T) -> T 16, count
  • Function: returns the total number of elements in a stream
  • Operation type: Terminal operation
  • Return type: long

For the sake of space, I will cover the basic calculation of streams here, but I will also focus on the creation of streams, numerical streams, and the use of Optional classes.


Welcome to add the author micro signal (DINGwPMZ), add group discussion, the author quality column catalog: 1, source analysis RocketMQ column (40 +) 2, source analysis Sentinel column (12 +) 3, source analysis Dubbo column (28 +) 4, source analysis Mybatis column 5, source analysis Netty column (18 +) 6, source analysis JUC column Source code analysis (MyCat) for Elasticjob