Is there one of the most complete Java 8 explanations that you can read first and then say

1, Basic knowledge

1) Why learn java 8

The changes made by Java 8 are more far-reaching in many ways than any change in Java history. These changes will make your programming easier

example:

Traditional writing:

List<Person> personList = Arrays.asList(new Person(21,50),new Person(22,55),new Person(23,60));
Collections.sort(personList, new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
        return o1.getWeight().compareTo(o2.getWeight());
    }
});
Copy code

Java 8 writing method:

personList.sort(Comparator.comparing(Person::getWeight));
Copy code

Students familiar with Linux operation should not default to this instruction:

cat testFile | tr "[A-Z]" "[a-z]" | sort | tail -3

This operation is based on stream operation. cat} will convert the file into a stream, then tr will convert the characters in the stream, sort will sort the rows in the stream, and tail -3 will output the last three rows of the stream. This is like a pipeline operation. After each transfer station, the processed results are transferred to the next processing center, and finally the final results are obtained.

The first programming idea of Java 8 is stream processing, which streams a series of data items and generates only one item at a time. The program can read the data items one by one from the input stream, and then write the data items to the output stream in the same way. The output stream of one program is likely to be the input stream of another program.

Function transfer

It is known that there are the following flowers in a set:

List<Flower> flowerList = Arrays.asList(new Flower("red", 6), new Flower("yellow", 7), new Flower("pink", 8));
Copy code

At this time, if I want safflower, the traditional writing is like this:

List<Flower> resList = new ArrayList<>();
for (Flower flower : flowerList) {
    if (StringUtils.equals("red", flower.getColor())) {
        resList.add(flower);
    }
}
Copy code

Well, if I want to spend less than 8 yuan, the writing is like this:

List<Flower> resList = new ArrayList<>();
for (Flower flower : flowerList) {
    if (flower.getPrice() < 8) {
        resList.add(flower);
    }
}
Copy code

In fact, most of the code writing methods are the same, but the judgment conditions are different. Then we will optimize the first version:

We extract the judgment methods:

public static boolean isRed(Flower flower) {
    return StringUtils.equals("red", flower.getColor());
}

public static boolean isLowPrice(Flower flower) {
    return flower.getPrice() < 8;
}
Copy code

With the help of the functional interface predict, pass in our customized methods:

public static List<Flower> filterFlower(List<Flower> flowers, Predicate<Flower> p) {
    List<Flower> resList = new ArrayList<>();
    for (Flower flower : flowers) {
        if (p.test(flower)) {
            resList.add(flower);
        }
    }
    return resList;
}
Copy code

use:

filterFlower(flowerList,Flower::isRed);
filterFlower(flowerList,Flower::isLowPrice);
Copy code

We can also transfer the function with the help of Lambda flow, so we don't have to write the judgment function in advance:

filterFlower(flowerList, (Flower f) -> StringUtils.equals("red", f.getColor()));
filterFlower(flowerList, (Flower f) -> f.getPrice() < 8);
Copy code

Default method

Before Java 8, we can implement an interface and then be forced to rewrite the method of this interface, which implies many problems: if an animal class adds a fly() method, the dog class does not need the fly method, but if it is not rewritten, it will compile and report an error. Therefore, the default method is also designed after Java 8, which skillfully solves this problem.

interface Animal {
    void eat();
    void fly();
}

class bird implements Animal {
    @Override
    public void eat() {}

    @Override
    public void fly() {
        System.out.println("bird fly");
    }
}

class dog implements Animal {
    @Override
    public void eat() {}
}
Copy code

After Java 8, it can be written as follows:

interface Animal {
    void eat();
    default void fly() {
        System.out.println("animal fly");
    }
}

class bird implements Animal {
    @Override
    public void eat() {}

    @Override
    public void fly() {
        System.out.println("bird fly");
    }
}

class dog implements Animal {
    @Override
    public void eat() {}
}
Copy code

These are some of the features of Java 8, so let's learn about the use of Java 8

2) Behavior parameterization

In development, we need to deal with the continuous demand. How to achieve adaptive scalability is what we should pay attention to.

Requirement 1: select red flowers

public static List<Flower> filterFlower(List<Flower> flowers) {
    List<Flower> resList = new ArrayList<>();
    for (Flower flower : flowers) {
        if (StringUtils.equals("red", flower.getColor())) {
            resList.add(flower);
        }
    }
}
Copy code

Requirement 2: select green words

Smart, you must have thought that we can filter flowers by passing a color parameter without modifying the main code every time.

public static List<Flower> filterFlowerByColor(List<Flower> flowers, String color) {
    List<Flower> resList = new ArrayList<>();
    for (Flower flower : flowers) {
        if (StringUtils.equals(color, flower.getColor())) {
            resList.add(flower);
        }
    }
}
Copy code

Demand 3: select flowers with a price less than 8 yuan

In this way, we can only write another method to realize this demand. In order to prevent subsequent price changes, we wisely set the price to variable parameters in advance.

public static List<Flower> filterFlowerByPrice(List<Flower> flowers, Integer price) {
    List<Flower> resList = new ArrayList<>();
    for (Flower flower : flowers) {
        if (flower.getPrice() < price) {
            resList.add(flower);
        }
    }
}
Copy code

In order to keep the code clean, we were forced to rewrite a method to realize the above requirements:

public static List<Flower> filterFlower(List<Flower> flowers, String color, Integer price, Boolean flag) {
    List<Flower> resList = new ArrayList<>();
    for (Flower flower : flowers) {
        if ((flag && flower.getPrice() < price) ||
            (!flag && StringUtils.equals(color, flower.getColor()))) {
            resList.add(flower);
        }
    }
    return resList;
}
Copy code

flag is used to control whether to screen price type flowers or color type flowers, but this writing method is really unsightly.

Well, since we can all pass the attribute of flowers as a parameter, can we also pass the behavior of filtering flowers as a parameter? Thinking about it, you move your hand:

First, define an interface for filtering behavior:

interface FilterPrecidate {
    boolean test(Flower flower);
}
Copy code

Then customize two behavior filtering classes to inherit this interface:

class RedColorFilterPredicate implements FilterPrecidate {
    @Override
    public boolean test(Flower flower) {
        return StringUtils.equals("red", flower.getColor());
    }
}

class LowPriceFilterPredicate implements FilterPrecidate {
    @Override
    public boolean test(Flower flower) {
        return flower.getPrice() < 8;
    }
}
Copy code

Then rewrite our filtering method by passing the behavior as a parameter:

public static List<Flower> filterFlower(List<Flower> flowers, FilterPrecidate filter) {
    List<Flower> resList = new ArrayList<>();
    for (Flower flower : flowers) {
        if (filter.test(flower)) {
            resList.add(flower);
        }
    }
    return resList;
}

/*****    Use*****/
filterFlower(flowerList,new RedColorFilterPredicate());
filterFlower(flowerList,new LowPriceFilterPredicate());
Copy code

In this way, our code is very clear, but let's look at the above method. The filterFlower() method can only pass objects as parameters, and the core method of the FilterPrecidate object is only test(). If we have new behavior, we need to create a new class to inherit the FilterPrecidate interface to implement the test() method. So, is there any way to directly pass the behavior of test() as a parameter? The answer is yes: Lomba

filterFlower(flowerList, (Flower flower) -> flower.getPrice() > 8);
Copy code

We can even pass multiple behaviors as a parameter:

filterFlower(flowerList, (Flower flower) -> flower.getPrice() > 8 && StringUtils.equals("red", flower.getColor()));
Copy code

It can be seen that behavior parameterization is a very useful pattern. It can easily use changing requirements. This pattern can encapsulate a behavior and parameterize the behavior of the method by passing and using the created behavior.

It can replace anonymous classes

If we sort a collection of flowers by price, we will do this:

Collections.sort(flowerList, new Comparator<Flower>() {
    @Override
    public int compare(Flower o1, Flower o2) {
        return o1.getPrice().compareTo(o2.getPrice());
    }
});
Copy code

Then, through behavior parameterization, we can write as follows:

Collections.sort(flowerList,(o1, o2) -> o1.getPrice().compareTo(o2.getPrice()));
Copy code

It can also be written as follows:

Collections.sort(flowerList, Comparator.comparing(Flower::getPrice));
Copy code

You can even write this:

flowerList.sort(Comparator.comparing(Flower::getPrice));
Copy code

Compared with the traditional way of writing, have you begun to love this way of writing

3) First met Lambda

Lambda can be understood as a concise representation of anonymous functions: it has no name, but it has parameter list, function body, return type, and an exception that can be thrown.

Lambda expressions encourage a style of behavior parameterization. Using lambda expressions, we can customize a Comparator object

Lambda example

  • (String s) - > s.length(): extract a value from an object. It has a String type parameter and returns an int type value. The Lambda expression has no return statement and implies return

  • (Flower f) - > f.getprice() > 8: Boolean expression. It has a parameter of Flower type and returns a value of boolean type

  • (String s) -> {System.out.print(s);} : consume an object with a string type parameter and no return value (void)

  • () - > new flower ("red", 8): create an object without passing in parameters, and return a value of type int (1)

Functional interface

A functional interface is an interface that defines only one abstract method and uses the @ FunctionalInterface tag.

For example:

  • public interface Comparator<T>{
        int compare(T o1, T o2);
    }
    Copy code
  • public interface Runnable{
        void run();
    }
    Copy code
  • public interface ActionListener extends EventListener{
    	void actionPerformed(ActionEvent e);
    }
    Copy code
  • public interface Callable<V>{
    	V call();
    }
    Copy code

Lambda expressions can directly provide the implementation of abstract methods of functional interfaces in the form of inline, and take the whole expression as an example of functional interfaces (lambda expressions are an example of concrete implementation of functional interfaces).

Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("This is the traditional way of writing");
        }
    };

Runnable r = () -> System.out.println("This is used Lambda Writing method of");
Copy code

Using functional interfaces

Predicate

This interface defines an abstract method of test(), which accepts generic t objects and returns a boolean. You can use this interface if you need to represent a boolean expression involving type T.

public static List<Flower> filterFlower(List<Flower> flowers, Predicate<Flower> p) {
    List<Flower> resList = new ArrayList<>();
    for (Flower flower : flowers) {
        if (p.test(flower)) {
            resList.add(flower);
        }
    }
    return resList;
}

/*****		Mode of use 		*****/
filterFlower(flowerList, (Flower flower) -> flower.getPrice() > 8);
Copy code

Consumer

This interface defines an abstract method of accept(). It accepts generic t objects without returning (void). You can use this interface if you need to access an object of type T and perform some operations on it.

List<Integer> nums = Arrays.asList(1,2,3,4);
nums.forEach(integer -> System.out.println(integer));
Copy code

Function

This interface defines an abstract method of apply(), which accepts generic T objects and returns a generic R object. If you need to define a Lambda to map the information of the input object to the output, you can use this interface.

(String s) -> s.length()
Copy code

Supplier

This interface defines an abstract method of get(). It does not pass in parameters and will return a generic T object. If you need to define a Lambda and output a custom object, you can use this interface.

Callable<Integer> call = () -> 1 ;
Copy code

Type check

Take this as an example:

filter(flowerList, (Flower flower) -> flower.getPrice() > 8);

  • First, find the declaration of the filter method
  • The second parameter is required to be an object of type predict
  • Predicate is a functional interface that defines an abstract method of test() and returns a boolean value

Type inference

filterFlower(flowerList, (Flower flower) -> flower.getPrice() > 8);

We can continue to simplify this code to:

filterFlower(flowerList, f -> f.getPrice() > 8);

Use local variables

Lambda expressions can use not only parameters in the body, but also free variables (variables defined in the outer scope).

int tmpNum = 1;
Runnable r = () -> System.out.println(tmpNum);
Copy code

Note: Lambda expressions can be used without restrictions for global and static variables, but for local variables, they must be declared final

Because instance variables are stored in the heap, while local variables are stored in the stack and are thread private. Lambda is used in a thread. Accessing a local variable is only accessing a copy of the variable, not the original value.

Method reference

Method reference is to let you create lambda expressions based on existing method implementations. Lambda's syntax sugar can be regarded as a single method.

example:

List<Flower> flowerList = Arrays.asList(new Flower("red", 6), new Flower("yellow", 7), new Flower("pink", 8));
Copy code
  • (Flower f)->f.getPrice(); ==> Flower::getPrice
  • flowerList.stream().map(t -> t.getPrice()).collect(Collectors.toList()); ===> flowerList.stream().map(Flower::getPrice).collect(Collectors.toList());
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
Copy code
  • nums.forEach(integer -> System.out.println(integer)); ===> nums.forEach(System.out::println);

How to build method references

  • Method reference to static method (sum method of Integer = = Integer::sum)
  • Method reference to any type of sample method (length of String = = String::length)
  • Method reference to the example method of an existing object (getPrice method of flower instance = = flower::getPrice)

Compound Lambda expression

Comparator composite

We have a collection of flowers as follows:

List<Flower> flowerList = Arrays.asList(new Flower("red", 6), new Flower("yellow", 7), new Flower("pink", 8), new Flower("white", 8));
Copy code

Sort by price of flowers:

flowerList.sort(Comparator.comparing(Flower::getPrice));
Copy code

In this way, the sub items are arranged in ascending order by default. If we want to enter the descending order, use reversed()

flowerList.sort(Comparator.comparing(Flower::getPrice).reversed());
Copy code

The price of pink flowers here is the same as that of white flowers. Then we can sort them by color after sorting the prices. What should we do: use thenpreparing()

flowerList.sort(Comparator.comparing(Flower::getPrice).thenComparing(Flower::getColor));
Copy code

Predicate composition

Interface for Predicate

  • negate: not
Predicate<Flower> redFlower = (t) -> StringUtils.equals("red",t.getColor());
Predicate<Flower> notRedFlower = redFlower.negate();
Copy code
  • And: and
Predicate<Flower> redFlower = (t) -> StringUtils.equals("red", t.getColor());
Predicate<Flower> lowPriceFlower = (t) -> t.getPrice() < 8;
Predicate<Flower> redAndLowPriceFlower = redFlower.and(lowPriceFlower);
Copy code
  • Or: or
Predicate<Flower> redFlower = (t) -> StringUtils.equals("red", t.getColor());
Predicate<Flower> lowPriceFlower = (t) -> t.getPrice() < 8;
Predicate<Flower> redOrLowPriceFlower = redFlower.or(lowPriceFlower);
Copy code

Function composition

For Function interface

  • andThen
Function<Integer, Integer> addRes = a1 -> a1 + 1;
Function<Integer, Integer> mulRes = a1 -> a1 * 2;
Function<Integer, Integer> andThenResult = addRes.andThen(mulRes);
Integer apply = andThenResult.apply(1);   // The result is 4 = = > (1 + 1) * 2
 Copy code
  • compose
Function<Integer, Integer> addRes = a1 -> a1 + 1;
Function<Integer, Integer> mulRes = a1 -> a1 * 2;
Function<Integer, Integer> composeResult = addRes.compose(mulRes);
Integer apply = composeResult.apply(1);  // The result is 3 = = > (1 * 2) + 1
 Copy code

The difference between the two is that the order of operations is different

2, Functional data processing

1) Use of stream

Collection is the most used API in Java. Stream is a new member of Java API, which allows data sets to be processed declaratively, and can be regarded as a high-level iterator to traverse data sets. Moreover, bangs can be processed transparently in parallel, so that there is no need to write any more multithreaded code.

Now there is a collection of flowers as follows:

List<Flower> flowerList = Arrays.asList(new Flower("red", 10), new Flower("yellow", 7), new Flower("pink", 8), new Flower("white", 8), new Flower("black", 12));
Copy code

Demand: get the color of flowers below 10 yuan and sorted according to the price

Traditional writing:

List<Flower> lowPriceFlowers = new ArrayList<>();
for (Flower flower : flowerList) {
    if (flower.getPrice() < 10) {
        lowPriceFlowers.add(flower);
    }
}
Collections.sort(lowPriceFlowers, new Comparator<Flower>() {
    @Override
    public int compare(Flower o1, Flower o2) {
        return o1.getPrice().compareTo(o2.getPrice());
    }
});
List<String> lowPriceFlowerColor = new ArrayList<>();
for (Flower priceFlower : lowPriceFlowers) {
    lowPriceFlowerNames.add(priceFlower.getColor());
}
Copy code

In order to fulfill this requirement, not only the amount of code is large, but also the temporary variable lowPriceFlowers , is defined. It's really terrible! After Java 8, the code should look like it:

List<String> colorList =  flowerList.stream().filter(t->t.getPrice()<10).sorted(Comparator.comparing(Flower::getPrice)).map(Flower::getColor).collect(Collectors.toList());
Copy code

The flowers below 10 yuan are filtered through the filter, sorted according to the price of the flowers through sorted, mapped out the color of the flowers through map, and finally reduced into a collection through collect. The result of filter processing is passed to the sorted method, then to the map method, and finally to the collect method.

We can even execute this code in parallel using multi-core architecture, just replace stream() with parallelStream()

flowerList.parallelStream().filter(t->t.getPrice()<10).sorted(Comparator.comparing(Flower::getPrice)).map(Flower::getColor).collect(Collectors.toList());
Copy code

Because operations such as filter, sorted, map , and , collect are high-level components independent of the specific thread model, their internal implementation can be single threaded or make full use of your multi-core architecture transparently! In practice, this means that you don't have to worry about threads and locks to make some data processing tasks parallel.

2) Streams and collections

The difference between a set and a flow is when it is calculated. A collection is an in memory data structure that contains all the current values in the data structure - each element in the collection must be calculated before it can be added to the collection. A stream is a conceptually fixed data structure (you can't add or delete elements), and its elements are calculated on demand. On the other hand, a stream is like a collection created late: values are calculated only when requested by the consumer.

Can only be traversed once: like iterators, a stream can only be traversed once. After traversal, the stream has been consumed. You can get a new stream from the original data source and go through it again.

List<String> color = Arrays.asList("red", "yellow", "pink");
Stream<String> s = title.stream();
s.forEach(System.out::println);		//Here the stream has been consumed
s.forEach(System.out::println);		//If there is no more consumption flow here, an error will be reported!
Copy code

3) Stream operation

The flow can be divided into three operations:

Get stream - > intermediate operation - > terminal operation

List<String> colorList =  flowerList.stream()					  //Get stream
                                    .filter(t->t.getPrice()<10)   //Intermediate operation
                                    .limit(3)					  //Intermediate operation
                                    .map(Flower::getColor)		  //Intermediate operation
                                    .collect(Collectors.toList());//Terminal operation
 Copy code

Intermediate operation:

operationReturn typeOperating parameters
filterStreamPredicate
mapStreamFuncation<T, R>
limitStream
sortedStreamComparator
distinctStream
skipStreamlong
limitStreamlong
flatMapStreamFuncation<T, Steam>

Terminal operation

operationReturn typeOperating parameters
forEachvoidConsumer
countlong
collectRCollector<T, A, R>
anyMatchbooleanPredicate
noneMatchbooleanPredicate
allMatchbooleanPredicate
findAnyOptional
findFirstOptional
reduceOptionalBinaryOperator

(1) Use stream

Filter: filter

List<String> colorList =  flowerList.stream().filter(t->t.getPrice()<10).collect(Collectors.toList());
Copy code

Filter de duplication: distinct

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println);
Copy code

Filter truncation: limit

List<String> colorList =  flowerList.stream().filter(t->t.getPrice()<10).limit(3).collect(Collectors.toList());
Copy code

Filter jumps: skip

List<String> colorList =  flowerList.stream().filter(t->t.getPrice()<10).skip(2).collect(Collectors.toList());
Copy code

(2) Mapping

The stream supports the {map() method, which takes a function as a parameter, and the number of rows will be applied to each element and mapped to a new element.

List<String> colors = flowerList.stream().map(Flower::getColor).collect(Collectors.toList());
Copy code

It creates a new set instead of modifying the original set

(3) Flattening of flow

Split a set of words into a set of letters:

[Hello,World] ===> [H, e, l, o, W, r, d]

First, we try to use map to see if it can solve the problem:

List<String> words = Arrays.asList("Hello","World");
words.stream().map(t->t.split("")).distinct().collect(Collectors.toList());
/*****		result 		*****
[[Ljava.lang.String;@2cdf8d8a, [Ljava.lang.String;@30946e09]
 **/
Copy code

We can see that the result of this processing is a collection of arrays, not the result we want, because the stream returned by the map is actually of type stream < string [] >. But what we want is stream < string > to represent a character stream.

Since the character stream of stream < string > is required, we use arrays Stream() to handle it, try:

words.stream().map(t -> t.split("")).map(Arrays::stream).distinct.collect(Collectors.toList());
/*****		result 		*****
[java.util.stream.ReferencePipeline$Head@1698c449, java.util.stream.ReferencePipeline$Head@5ef04b5]
 **/
Copy code

This is a collection of stream < string > returned. It seems that the problem can be solved by merging this collection. So flatMap() appears.

words.stream().map(t->t.split("")).flatMap(t -> Arrays.stream(t)).distinct().collect(Collectors.toList());
/*****		result 		*****
[H, e, l, o, W, r, d]
 **/
Copy code

Sure enough, the problem has been solved successfully. The flatMap method is to let you convert each value in one stream into another stream, and then connect all streams into one stream.

(4) Match

  • anyMatch()

    Whether there is an element in the stream that can match the given predicate. If there is only one match, it will return true

boolean res = flowerList.stream().anyMatch(t -> t.getPrice() < 8);
Copy code
  • allMatch()

    Whether the elements in the flow can match the given predicate, and true can be returned on all matches

boolean res = flowerList.stream().allMatch(t -> t.getPrice() < 8);
Copy code
  • noneMatch()

    If no element in the stream matches the given predicate, a match will return false

boolean res = flowerList.stream().noneMatch(t -> t.getPrice() < 8);
Copy code

(5) Search

  • findAny

    Returns any element in the current stream

flowerList.stream().filter(t->t.getPrice()<8).findAny();
Copy code
  • findFirst

    Returns the first element in the current stream

flowerList.stream().filter(t->t.getPrice()<8).findFirst();
Copy code

(6) Reduction

reduce} receives two parameters, one is the initial value, and the other is the operation of combining all elements in the collection

reduce also supports a parameter that combines all elements in the collection, but returns an Option, which will be described below

List<Integer> nums = Arrays.asList(1,2,3,4,5,6,7,8,9);

Element summation

Traditional writing:

int res = 0;
for (Integer num : nums) {
    res += num;
}
Copy code

After improvement:

// Two parameter version
int res = nums.stream().reduce(0,(a, b) -> a + b);
int res = nums.stream().reduce(0,Integer::sum);
// A parametric version
Optional<Integer> o = nums.stream().reduce(Integer::sum);
Copy code

Maximum and minimum

Traditional writing:

int max = 0;
int min = Integer.MAX_VALUE;
for (Integer num : nums) {
    if (num > max) {
        max = num;
    }
    if (num < min) {
        min = num;
    }
}
Copy code

After improvement:

// Two parameter version
int max = nums.stream().reduce(0,Integer::max);
int min = nums.stream().reduce(Integer.MAX_VALUE,Integer::min);
// A parametric version
Optional<Integer> maxOption = nums.stream().reduce(Integer::max);
Optional<Integer> minOption = nums.stream().reduce(Integer::min);
Copy code

(7) Small exercise (online)

(1) Identify all transactions that occurred in 2011 and rank them by transaction volume (from low to high). (2) what different cities have traders worked in? (3) Find all traders from Cambridge and sort by name. (4) Returns the name strings of all traders, sorted alphabetically. (5) Do any traders work in Milan? (6) Print all trading volumes of traders living in Cambridge. (7) What is the highest transaction volume of all transactions? (8) Find the transaction with the smallest transaction amount

answer:

4) Construction of flow

  • Create stream from value: stream of()/Stream. empty()
Stream<String> stream = Stream.of("hello","world");
Stream<String> emptyStream = Stream.empty();
Copy code
  • Create stream from array: arrays stream()
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
Copy code
  • Generate stream from file: file lines()
long uniqueWords = 0;
try(Stream<String> lines =
Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
}catch(IOException e){
}
// Use files Lines gets a stream in which each element is a line in a given file. Then, you can call the split method on line to split the line into words
 Copy code

5) Use of collectors

Now there is a collection of flowers as follows:

List<Flower> flowerList = Arrays.asList(new Flower("red", 10), new Flower("yellow", 7), new Flower("pink", 8), new Flower("yellow", 8), new Flower("red", 12));
Copy code

At this time, I want to classify the flowers according to their colors and get a map < string, list < flower > >

Traditional writing:

Map<String, List<Flower>> listMap = new HashMap<>();
for (Flower flower : flowerList) {
    if (null == listMap.get(flower.getColor())) {
        List<Flower> flowers = new ArrayList<>();
        listMap.put(flower.getColor(), flowerList);
    }
    listMap.get(flower.getColor()).add(flower);
}
Copy code

I believe the above code is common. Is there any better way to write it after learning Java 8:

Map<String,List<Flower>> map = flowerList.stream().collect(Collectors.groupingBy(Flower::getColor));
Copy code

One line of code solution, Java 8 is really a show!

One of the main advantages of functional expression is that we just tell it "what to do" without paying attention to * "how to do" *. As in the previous example, we need to group by color, so we just tell the collector to group by color (collectors. Groupingby (flower:: getcolor)). What we often use above is collect(Collectors.toList()), which is used to collect the results we need into a collection.

Used to calculate the total:

Long c1 = flowerList.stream().collect(Collectors.counting());
//You can also count directly with the count() method
Long c2 = flowerList.stream().count();
Copy code

Used to find the maximum and minimum values:

Optional<Flower> max = flowerList.stream().collect(Collectors.maxBy(Comparator.comparing(Flower::getPrice)));
Optional<Flower> min = flowerList.stream().collect(Collectors.minBy(Comparator.comparing(Flower::getPrice)));
Copy code

Used to sum:

Integer sum = flowerList.stream().collect(Collectors.summingInt(Flower::getPrice));
Copy code

Average:

Double avg = flowerList.stream().collect(Collectors.averagingInt(Flower::getPrice));
Copy code

String used to connect:

String color = flowerList.stream().map(Flower::getColor).collect(Collectors.joining(", "));
Copy code

6) Use of groups

Now there is a collection of flowers as follows:

List<Flower> flowerList = Arrays.asList(new Flower("red", 10), new Flower("yellow", 7), new Flower("pink", 8), new Flower("yellow", 8), new Flower("red", 12));

/*****		result 		*****
{red=[Flower(color=red, price=10), Flower(color=red, price=12)], pink=[Flower(color=pink, price=8)], yellow=[Flower(color=yellow, price=7), Flower(color=yellow, price=8)]}
 **/
Copy code

Group by color: Map < string, list < flower > >

Map<String,List<Flower>> color = flowerList.stream().collect(Collectors.groupingBy(Flower::getColor));
/*****		result 		*****
{red=[Flower(color=red, price=10), Flower(color=red, price=12)], pink=[Flower(color=pink, price=8)], yellow=[Flower(color=yellow, price=7), Flower(color=yellow, price=8)]}
 **/
Copy code

Count the number of each color: Map < string, long >

Map<String, Long> longMap = flowerList.stream().collect(Collectors.groupingBy(Flower::getColor, Collectors.counting()));
/*****		result 		*****
{red=2, pink=1, yellow=2}
 **/
Copy code

Multi level grouping can also be supported

Group by color first, then by price: Map < string, map < string, list < flower > > >

Map<String, Map<String, List<Flower>>> collect = flowerList.stream().collect(Collectors.groupingBy(Flower::getColor, Collectors.groupingBy(t -> {
    if (t.getPrice() < 8) {
        return "LOW_PRICE";
    } else {
        return "HIGHT_PRICE";
    }
})));
/*****		result 		*****
{red={HIGHT_PRICE=[Flower(color=red, price=10), Flower(color=red, price=12)]}, pink={HIGHT_PRICE=[Flower(color=pink, price=8)]}, yellow={HIGHT_PRICE=[Flower(color=yellow, price=8)], LOW_PRICE=[Flower(color=yellow, price=7)]}}
 **/
Copy code

First group by color, and then find the most expensive flower in each color: Map < string, flower >

Map<String, Flower> f = flowerList.stream().collect(Collectors.groupingBy(Flower::getColor, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Flower::getPrice)), Optional::get)));
/*****		result 		*****
{red=Flower(color=red, price=12), pink=Flower(color=pink, price=8), yellow=Flower(color=yellow, price=8)}
 **/
Copy code

The factory method takes two parameters -- the collector to be converted and the conversion function, and returns another collector. This collector is equivalent to a wrapper of the old collector. The last step of the collect operation is to map the return value with the conversion function. Here, the wrapped collector is the one established with maxBy, and the conversion function Optional::get extracts the value in the returned Optional.

Common methods of Collectors

methodReturn typepurpose
toListListCollect all items in the stream into a List
toSetSetCollect all items in the stream into a Set and delete duplicates
toCollectionCollectionCollect all items in the flow into a collection created by a given supply source
countingLongCount the number of elements in the flow
summingIntIntegerSumming an integer attribute of an item in a stream
averagingIntDoubleCalculates the average value of the item Integer property in the flow
joiningStringThe string generated by calling the toString method for each item in the connection stream
maxByOptionalAn option that wraps the largest element selected in the stream according to the given comparator. If it is empty, it is Optional empty()
minByOptionalAn option that wraps the smallest element selected in the stream according to the given comparator. If it is empty, it is Optional empty()
reducingType generated by reduction operationStarting with an initial value as an accumulator, BinaryOperator is combined with groups of elements in the stream to reduce the stream to a single value
collectingAndThenThe type returned by the conversion functionWrap another collector and apply a conversion function to its result
groupingByMap<K, List>According to the value of one attribute of the item, the item in the stream is used as a group, and the attribute value is used as the key of the result Map

3, Learn to use Optional

The most frequently encountered exception in development is too NullPointException. Because this is the price we pay for facilitating and even inevitable constructs such as null references. There seems to be a turnaround after Java 8, that is, using Optional instead of null.

At first glance, there should be no problem with the above code. During normal development, you may be tempted to write code like this. But the question is, does everyone really have a mobile phone? If new person() If getphone () can't get the phone, will a familiar NullPointException occur when calling getType().

1) Defensive inspection

In order to avoid null pointer exceptions, the Optional in Java 8 is well avoided for us.

Classic prevention

private String getPhoneType(Person person) {
    if (person != null) {
        Phone phone = person.getPhone();
        if (phone != null) {
            return phone.getType();
        }
    }
    return "";
}
Copy code

The null judgment operation is performed once for each reference, and the effect must be good. It can also avoid null pointer exceptions. At that time, every air judgment had to add a {judgment, which really made people bigger.

Optional prevention

As can be seen from the figure, the "Optional" is equivalent to a container, which can hold T-type objects. When the variable does not exist, the missing value will be modeled as an "empty" Optional object by the method Optional Empty() returns. This is Optional The difference between empty () and null is that if you reference a null, the result will certainly trigger a NullPointException exception, but you reference Optional Empty () is fine.

The above code can be modified to:

private String getPhoneType(Person person) {
    return Optional.ofNullable(person).map(Person::getPhone).map(Phone::getType).orElse("");
}
Copy code

One line of code, clean and tidy.

2) Learn to use Option

Create an Optional object

Create an empty Optional

Optional<Person> personOpt = Optional.empty()

Create a non empty Optional

Optional<Person> personOpt = Optional.of(person)

Optional.of() does not accept null values. If person is a null value, a NullPointException will be thrown instead of waiting for you to try to access the person's property.

Create an Optional that accepts null values

Optional<Person> personOpt = Optional.ofNullable(Person)

If person is null, the resulting Optional object is an empty object.

Use map

The map() method in Optional is similar to the map() method in the stream, which extracts and converts values from the Optional object.

Optional<String> name = Optional.ofNullable(person).map(Person::getName);
Copy code

An Optional object is obtained to prevent getting a null. We can use the Optional Get() to get the value.

Default behavior

We can use the get() method to get the Optional value, or use orElse() to define a default value. When an empty Optional value is encountered, the default value will be used as the return value of the method call. The following are common methods for Optional:

  • get()

The simplest but most unsafe method. If the variable exists, it will directly return the encapsulated variable value. Otherwise, it will throw a NullpointException exception.

  • orElse(T other)

You are allowed to define a default value and return it when option is empty.

  • orElseGet(Supplier<? extend T> other)

Is a deferred call version of the orElse() method, which is called when the Optional object does not contain a value.

  • orElseThrow(Supplier<? extend X> excetionSupplier)

Similar to the get() method, an exception will be thrown when the Optional object is empty, but this exception can be customized.

  • ifPresent(Consumer<? extend T>)

The method executed in the presence of the Optional object, and vice versa. An empty parameter is also accepted if

methoddescribe
emptyReturns an empty Optional instance
filterIf the value exists and satisfies the provided predicate, the Optional object containing the value will be returned; Otherwise, an empty Optional object is returned
getIf the value exists, the value will be returned in Optional encapsulation, otherwise a NullPointException exception will be thrown
ifPresentIf the value exists, the method call using the value is executed, otherwise nothing is done
ifPresentReturns true if the value exists; otherwise, returns false
mapIf the value exists, the provided mapping function call is performed on the value
ofThe specified value is returned after being encapsulated with Optional. If the value is null, a NullPointException exception will be thrown
ofNullableThe specified value is returned after being encapsulated with Optional. If the value is null, an empty Optional object is returned
orElseIf there is a value, it is returned; otherwise, a default value is returned
orElseGetIf there is a value, return it; otherwise, return a value generated by the specified Supplier interface
orElseThrowIf there is a value, put it back, otherwise throw an exception generated by the specified Supplier interface

4, New date and time

Before Java 8, our support for date and time was intelligently dependent on Java util. Date class, which cannot represent date, but can only represent time in milliseconds. And its expression is not so intuitive, in Java 1 In the date class of 0, the start of the year is 1900 and the start of the month is 0. If we want to construct a date on July 18, 2020 at this time, we have to do this:

Date date = new Date(120, 6, 18);
System.out.println(date);   // Sat Jul 18 00:00:00 CST 2020
 Copy code

This kind of construction is terrible, isn't it? It's too unfriendly for people who don't know Date. In Java 1 The Calender class appeared after 1, and most of the methods in Date were abandoned. However, there are similar problems and design defects in Calender class, and it is sometimes difficult for us to choose which of the two Date classes to use.

LocalDate

Create a LocalDate object

LocalDate nowDate = LocalDate.of(2020,7,18);    //2020-07-18
int year = nowDate.getYear();                   //2020
Month month = nowDate.getMonth();               //07
int day = nowDate.getDayOfMonth();              //18
DayOfWeek dayOfWeek = nowDate.getDayOfWeek();   //SATURDAY
int days = nowDate.lengthOfMonth();             //31
LocalDate nowdate = LocalDate.now();            //Get current time > July 18, 2020
 Copy code

You can also use TemporalField to read the value of LocalDate

LocalDate nowDate = LocalDate.of(2020,7,18);        //2020-07-18
int year = nowDate.get(ChronoField.YEAR);           //2020
int month = nowDate.get(ChronoField.MONTH_OF_YEAR); //07
int day = nowDate.get(ChronoField.DAY_OF_MONTH);    //18
 Copy code

LocalTime

Create a LocalTime object

LocalTime nowTime = LocalTime.of(19, 34, 32);  //19:34:32
int hour = nowTime.getHour();                  //19
int minute = nowTime.getMinute();              //34
int second = nowTime.getSecond();              //32
 Copy code

You can also use TemporalField to read the value of LocalTime

LocalTime nowTime = LocalTime.of(19, 34, 32);           //19:34:32
int hour = nowTime.get(ChronoField.HOUR_OF_DAY);        //19
int minute = nowTime.get(ChronoField.MINUTE_OF_HOUR);   //34
int second = nowTime.get(ChronoField.SECOND_OF_MINUTE); //32
 Copy code

LocalDateTime

LocalDateTime is equivalent to merging date and time. The following are the ways to create it:

LocalDate nowDate = LocalDate.of(2020,7,18);    //2020-07-18
LocalTime nowTime = LocalTime.of(19, 45, 20);   //19:34:32
LocalDateTime dt1 = LocalDateTime.of(2020, Month.JULY, 18, 19, 45, 20);
LocalDateTime dt2 = LocalDateTime.of(nowDate, nowTime);
LocalDateTime dt3 = nowDate.atTime(19, 45, 20);
LocalDateTime dt4 = nowDate.atTime(nowTime);
LocalDateTime dt5 = nowTime.atDate(nowDate);

LocalDate date1 = dt1.toLocalDate();		//2020-07-18
LocalTime time1 = dt1.toLocalTime();		//19:45:20
 Copy code

Date of time point  general method of time class:

Method nameStatic methoddescribe
fromyesCreate an object instance based on the incoming temporary object
nowyesCreate a temporary object based on the system clock
ofyesAn instance of a temporary object is created by a part of the object
parsenoCreates an instance of a temporary object from a string
atOffsetnoCombine a Temporal object with a time zone offset
atZonenoCombine a Temporal object with a time zone
formatnoConverts a temporary object to a string using a specified formatter (this method is not provided by the Instant class)
getnoReads the value of a part of a temporary object
minusnoCreate a copy of the temporary object by subtracting a certain length of time from the value of the current temporary object
plusnoCreate a copy of the temporary object by adding a certain duration to the value of the current temporary object
withnoUse the temporary object as a template to modify some states and create a copy of the object

Duration and Period

These two classes are used to represent the interval between two times

Duration d1 = Duration.between(time1, time2);
Duration d1 = Duration.between(dateTime1, dateTime2);
Duration threeMinutes = Duration.ofMinutes(3);
Duration threeMinutes = Duration.of(3, ChronoUnit.MINUTES);

Period tenDays = Period.ofDays(10);
Period threeWeeks = Period.ofWeeks(3);
Period twoYearsSixMonthsOneDay = Period.of(2, 6, 1);
Copy code

General method for representing time interval in date time class:

Method nameStatic methodMethod description
betweenyesCreate an interval between two points in time
fromyesAn interval is created by a temporary point in time
ofyesAn instance of interval is created from its components
parseyesCreates an instance of interval from a string
addTonoCreate a copy of the interval and overlay it on a specified temporary object
getnoRead the status of the interval
isNegativenoCheck whether the interval is negative and does not contain zero
isZeronoCheck whether the duration of the interval is zero
minusnoWear a copy of the interval by subtracting a certain time
multipliedBynoMultiply the value of interval by a scalar to create a copy of the interval
negatednoCreate a copy of the interval in a way that ignores a certain length of time
plusnoCreate a copy of the interval by increasing a specified duration
subtractFromnoSubtracts the interval from the specified temporary object

Thank you for your reading. I hope it can help you!

For those who need to get free materials, add a little assistant vx: soxwv # to get materials for free!

Keywords: Java Back-end Programmer architecture

Added by Twysted on Fri, 31 Dec 2021 04:32:52 +0200