GitHub JavaEgg, N line Internet development essential skills weapon spectrum

Java8 was released back in March 2014, 6 years ago. Have you read up on it

This article is to use my poor English and shameless copy that copy, come out, not deep source code, just have an overall understanding of Java8, can be used, example code is also on Github

What’s new in JDK 8

  • Java Programming Language

    • Lambda expressions: A new language feature that enables you to treat functions as method parameters or code as data
    • Method references: Method references provide easy-to-read lambda expressions for methods that already have names
    • Default methods: Define default methods (implemented methods) for the interface using the default keyword
    • Repeated annotations provide the ability to apply the same annotation to the same declaration or type multiple times
    • Type annotations provide the ability to apply annotations anywhere a type is used, not just on declarations
    • Java8 enhances type inference
    • Method parameter reflection
    • java.util.function: a new package that contains generic functional interfaces that provide target types for lambda expressions and method references
  • Collections

    • java.util.streamAdded to packageStream APIIs used to support functional operations on element streams
    • Improved hashMaps with key conflicts
  • Compact Runtime Profiles

  • Security

  • JavaFX

  • Tools (includes some calls to the Nashorn engine, launching JavaFX applications, etc.)

  • Internationalization

    • Unicode enhancements, including Unicode 6.2.0 support
    • New Calendar and Locale apis are provided
  • Deployment

  • Date-time Package: Provides more comprehensive Time and Date operations

  • Scripting: Java 8 provides a new Nashorn javascript engine (replacing the Nashorn javascript engine) that allows us to run specific javascript applications on the JVM

  • Improved IO and NIO

  • Improvements to java.lang and java.util

    • Supports parallel array sorting
    • Support Base64 encoding and decoding
    • Support for unsigned operations
    • Optional class: Minimizes null pointer exceptions
  • JDBC

    • The JDBC-ODBC bridge has been removed
    • JDBC 4.2 introduces new features
  • Java DB (a Java database)

  • Networking

    • A newjava.net.URLPermission
  • Concurrency

    • CompletableFutureEnhanced the previous oneFuture
    • java.util.concurrent.ConcurrentHashMapSupport for aggregation operations based on the newly added Streams feature and lambda expressions
    • java.util.concurrent.atomicProvides a set of atomic variable classes that support lock-free, thread-safe operations on individual variables
    • java.util.concurrent.ForkJoinPoolUsed to supplement the ExecutorService
    • java.util.concurrent.locks.StampedLockFunction-based locks are provided with three modes for controlling read/write access
  • JVM: Removed PermGen and replaced with Metaspace

Particularly powerful in Java8 are Lambda expressions and streams, through which many new and enhanced packages are added

New: Java.lang. Invoke, java.util. Function, java.util. Stream

Modification:


Lambda expressions

Lambda expressions can be thought of as a succinct way of representing anonymous functions that are transitive. Lambda expressions get their name from the λ calculus in mathematics: they have no name, but have a list of arguments, a function body, a return type, and possibly a list of exceptions that can be thrown.

  • Anonymous – Anonymous functions (i.e. functions without a function name), unlike normal methods that have a clear name, “write less, think more”
  • Functions – Lambda functions do not belong to a specific class like methods, but do have argument lists, function bodies, and return types
  • Pass – Lambda expressions can be passed as arguments to methods or stored in variables
  • Brevity – you don’t have to write as much template code as anonymous classes

Lambda expressions enable you to encapsulate individual units of behavior and pass them on to other code. Lambda expressions can be used if you want to perform an operation on each element of the collection, when the process completes, or when the process encounters an error.

1. Why Lambda expressions

Lambda is an anonymous function, and we can think of a Lambda expression as a piece of code that can be passed (passing code like data — parameterizing behavior). You can write cleaner, more flexible code. As a more compact code style, the language expression ability of Java has been improved.

One problem with anonymous classes is that if your implementation of anonymous classes is very simple, such as an interface containing only one method, the syntax of anonymous classes can seem clumsy and unclear. In these cases, you often try to pass functionality as a parameter to another method, such as what to do when someone clicks a button. Lambda expressions let you do this by treating functions as method parameters or code as data.

2. Lambda expression syntax

(parameters) -> expression or (parameters) ->{statements; }

Lambda expressions introduce a new syntactic element and operator into the Java language. The operator is “->”, which is called the Lambda or clipping operator. It breaks Lambda into two parts:

  • Left: specifies all the arguments needed for a Lambda expression

  • Right: specifies the body of the Lambda, which is the function that the Lambda expression is to perform

Eg (Wrong demonstration) :

(Integer i) -> return "hello"+i;   // error Lambda, return is a control flow statement that requires {}
(String s) -> {"hello"; }// "hello" is an expression, not a statement. {return "hello" can be written as {return "hello"; }
Copy the code

Eg (Correct demonstration) :

  1. With no arguments and no return value, the Lambda body requires only one statement

    Runnable runnable = () -> System.out.println("hello lambda");
    Copy the code
  2. Lambda takes one argument

    Consumer<String> consumer = (args) -> System.out.println(args);
    Copy the code

    When Lambda requires only one argument, the argument’s parentheses can be omitted

    Consumer<String> consumer = args -> System.out.println(args);
    Copy the code
  3. Lambda takes two arguments and has a return value

    BinaryOperator<Long> binaryOperator = (Long x,Long y) -> {
    	System.out.println("Implement function interface methods");
    	return x +y;
    };
    Copy the code

    The data type of the argument can be omitted, Java8 enhances type inference, and return and braces can be omitted when the Lambda body has only one statement

    BinaryOperator<Long> binaryOperator = (x, y) -> x + y;
    Copy the code

Type inference

The types of arguments in Lambda expressions above are inferred by the compiler. Programs can compile without specifying a type in a Lambda expression, because Javac behind the scenes deduces the type of the argument based on the context of the program. The type of a Lambda expression is context-dependent and inferred by the compiler. This is called “type inference”. The diamond operator (<>), introduced in Java7, uses generics to infer types from context.

List<String> list = new ArrayList<>();
Copy the code

3. Lambda expression instance

As the official example, suppose you want to develop a social network software, and the PM who is short of work changes his needs all day. One day he has to query the information of adult users, the next day he has to query the information of adult women, and the next day he has to query the information of all kinds of strange search criteria.

The programmer at this time: from simple user traverse comparison method to general search method to later use factory mode, wait until the 7th day, you are impatient, Mader, each condition is a sentence, I wrote 7 classes, I don’t want to be a CtrlCV engineer, Lambda expression is your only choice at this time.

Behavior parameterization is a software development pattern that helps you deal with frequently changing requirements.

A step-by-step demo of the benefits of using Java8 (from value parameterization to behavior parameterization). code

import java.util.List;
import java.util.ArrayList;
import java.time.chrono.IsoChronology;
import java.time.LocalDate;
public class Person {
    public enum Sex {
        MALE, FEMALE
    }

    String name;
    LocalDate birthday;
    Sex gender;
    String emailAddress;

    Person(String nameArg, LocalDate birthdayArg,
           Sex genderArg, String emailArg) {
        name = nameArg;
        birthday = birthdayArg;
        gender = genderArg;
        emailAddress = emailArg;
    }

    public int getAge(a) {
        return birthday
                .until(IsoChronology.INSTANCE.dateNow())
                .getYears();
    }

    public void printPerson(a) {
        System.out.println(name + "," + this.getAge());
    }

    public Sex getGender(a) {
        return gender;
    }

    public String getName(a) {
        return name;
    }

    public String getEmailAddress(a) {
        return emailAddress;
    }

    public LocalDate getBirthday(a) {
        return birthday;
    }

    public static int compareByAge(Person a, Person b) {
        return a.birthday.compareTo(b.birthday);
    }

    public static List<Person> createRoster(a) {
        List<Person> roster = new ArrayList<>();
        roster.add(new Person(
                        "Fred",
                        IsoChronology.INSTANCE.date(1980.6.20),
                        Person.Sex.MALE,
                        "[email protected]"));
        roster.add(new Person(
                        "Jane",
                        IsoChronology.INSTANCE.date(1990.7.15),
                        Person.Sex.FEMALE, "[email protected]"));
        roster.add(new Person(
                        "George",
                        IsoChronology.INSTANCE.date(1991.8.13),
                        Person.Sex.MALE, "[email protected]"));
        roster.add(new Person(
                        "Bob",
                        IsoChronology.INSTANCE.date(2000.9.12),
                        Person.Sex.MALE, "[email protected]"));
        returnroster; }}Copy the code

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

public class RosterTest {
    interface CheckPerson {
        boolean test(Person p);
    }

    /** * 1. Eg: Output members older than 20 * a way to match members that match a certain characteristic * What if the boss wants members within a certain range? And then I'm going to do */
    public static void printPersonsOlderThan(List<Person> roster, int age) {
        for (Person p : roster) {
            if(p.getAge() >= age) { p.printPerson(); }}}/** * 2. eg: Output members aged between 14 and 30 * more comprehensive matching method * What if the boss only wants male members? * /
    public static void printPersonsWithinAgeRange(
            List<Person> roster, int low, int high) {
        for (Person p : roster) {
            if(low <= p.getAge() && p.getAge() < high) { p.printPerson(); }}}/** * 3. What's the matter with the boss? * Method 1: specify the search criteria code in the local class, through the interface, different requirements corresponding to different implementation classes, * each time to create a new implementation class, write a lot of code * method 2: To specify search criteria in an anonymous class, you don't need to write a variety of implementations, but you do need to write an interface CheckPerson, * and anonymous classes can be quite cumbersome to write * method 3: Lambda expressions are a good choice for lazy people. CheckPerson is an interface that contains only one abstract method, * so simple that Lambda can omit its implementation */
    public static void printPersons( List
       
         roster, CheckPerson tester)
        {
        for (Person p : roster) {
            if(tester.test(p)) { p.printPerson(); }}}/** * 4. /** * 4. * You can also use the standard function interface instead of CheckPerson, To further reduce the amount of code required * the java.util.function package defines the standard function interface * We can use the Predicate
      
        interface provided by JDK8 instead of CheckPerson. * This interface contains method Boolean test(T T) */
      
    public static void printPersonsWithPredicate( List
       
         roster, Predicate
        
          tester)
        
        {
        for (Person p : roster) {
            if(tester.test(p)) { p.printPerson(); }}}Lambda expressions don't just simplify anonymous classes * simplify P.printPerson (), * use the void Accept (T T) method of the Consumer
      
        interface, which is equivalent to the input operation */
      
    public static void processPersons( List
       
         roster, Predicate
        
          tester, Consumer
         
           block)
         
        
        {
        for (Person p : roster) {
            if(tester.test(p)) { block.accept(p); }}}Function
      
        = Function
       
         = Function
        
          = Function
        ,r>
       ,r>
      ,r>
    public static void processPersonsWithFunction( List
       
         roster, Predicate
        
          tester, Function
         
           mapper, Consumer
          
            block)
          
         ,>
        
        {
        for (Person p : roster) {
            if(tester.test(p)) { String data = mapper.apply(p); block.accept(data); }}}// use generics
    public static <X, Y> void processElements( Iterable
       
         source, Predicate
        
          tester, Function
         
           mapper, Consumer
          
            block)
          
         ,>
        
        {
        for (X p : source) {
            if(tester.test(p)) { Y data = mapper.apply(p); block.accept(data); }}}public static void main(String[] args) {
        List<Person> roster = Person.createRoster();

        /** * 1. Output members older than 20 */
        System.out.println("Persons older than 20:");
        printPersonsOlderThan(roster, 20);
        System.out.println();

        /** * 2. Output members aged between 14 and 30 */
        System.out.println("Persons between the ages of 14 and 30:");
        printPersonsWithinAgeRange(roster, 14.30);
        System.out.println();

        /** * 3. Output male members between the ages of 18 and 25 * (specify search criteria in the local class) * You can use an anonymous class instead of a local class, and you don't have to declare a new class for every search */
        System.out.println("Persons who are eligible for Selective Service:");
        class CheckPersonEligibleForSelectiveService implements CheckPerson {
            public boolean test(Person p) {
                return p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25; }}// This is actually passing code through behavior parameterization
        printPersons(
                roster, new CheckPersonEligibleForSelectiveService());

        System.out.println();

        // 3. Specify the search criteria code in the anonymous class
        System.out.println("Persons who are eligible for Selective Service " +
                "(anonymous class):");
        printPersons(
                roster,
                new CheckPerson() {
                    public boolean test(Person p) {
                        return p.getGender() == Person.Sex.MALE
                                && p.getAge() >= 18
                                && p.getAge() <= 25; }}); System.out.println();// 3: Simplifies code using Lambda expressions, one arrow
        System.out.println("Persons who are eligible for Selective Service " +
                "(lambda expression):");

        printPersons(
                roster,
                (Person p) -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

        System.out.println();

        // 4. Use Lambda's standard functional interface
        System.out.println("Persons who are eligible for Selective Service " +
                "(with Predicate parameter):");

        printPersonsWithPredicate(
                roster,
                p -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25
        );

        System.out.println();

        //5. Use Predicate and Consumer parameters
        System.out.println("5. Persons who are eligible for Selective Service " +
                "(with Predicate and Consumer parameters):");

        processPersons(
                roster,
                p -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25,
                p -> p.printPerson()
        );

        System.out.println();

        Function
      
        specifies the output type
      ,r>
        System.out.println("Persons who are eligible for Selective Service " +
                "(with Predicate, Function, and Consumer parameters):");

        processPersonsWithFunction(
                roster,
                p -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25,
                p -> p.getEmailAddress(),
                email -> System.out.println(email)
        );

        System.out.println();

        // use generics
        System.out.println("Persons who are eligible for Selective Service " +
                "(generic version):");

        processElements(
                roster,
                p -> p.getGender() == Person.Sex.MALE
                        && p.getAge() >= 18
                        && p.getAge() <= 25,
                p -> p.getEmailAddress(),
                email -> System.out.println(email)
        );

        System.out.println();

        // 8: Uses batch data operations that accept Lambda expressions
        System.out.println("Persons who are eligible for Selective Service " +
                "(with bulk data operations):");

        roster.stream()
                .filter(
                        p -> p.getGender() == Person.Sex.MALE
                                && p.getAge() >= 18
                                && p.getAge() <= 25)
                .map(p -> p.getEmailAddress())
                .forEach(email -> System.out.println(email));
        System.out.println();

        /** * 9. Java 8 required the implementation of the Comparator interface * The interface Comparator is a functional interface. Therefore, * can use lambda expressions instead of defining and creating a new instance of the class that implements the Comparator: */
        Person[] rosterAsArray = roster.toArray(new Person[roster.size()]);

        Arrays.sort(rosterAsArray,
                (a, b) -> Person.compareByAge(a, b)
        );

        for (Person person : roster) {
            person.printPerson();
        }

        /** * This method of comparing birth dates of two Person instances already exists as Person. * Comparebyage. You can call this method */ in a lambda expression

        Arrays.sort(rosterAsArray,
                (a, b) -> Person.compareByAge(a, b)
        );
}
Copy the code

2. Functional interfaces

1. What are functional interfaces

  • An interface that contains only one abstract method is called a functional interface, and that abstract method is also called a function method. The familiar comparators, Runnable, and Callable are functional interfaces.
  • Such an interface is so simple that it is not worth defining in an application, so JDK8 doesjava.util.functionSeveral standard functional interfaces are defined for our use in the.Package java.util.function
  • Objects of this interface can be created using Lambda expressions. If a Lambda expression throws a checked exception, the exception needs to be declared on the abstract method of the target interface.
  • We can use the @functionalinterface annotation on any FunctionalInterface to check if it is a FunctionalInterface, and javadoc will include a declaration that the interface is a FunctionalInterface.

2. Custom functional interface

@FunctionalInterface    // @functionalinterface annotation this interface is designed to be a FunctionalInterface, otherwise it will compile errors
public interface MyFunc<T> {
    T getValue(T t);
}
Copy the code
public static String toUpperString(MyFunc<String> myFunc, String str) {
    return myFunc.getValue(str);
}

public static void main(String[] args) {
    String newStr = toUpperString((str) -> str.toUpperCase(), "abc");
    System.out.println(newStr);
}
Copy the code

Pass a Lambda expression as a parameter: In order to pass a Lambda expression as a parameter, the parameter type that receives the Lambda expression must be the type of the functional interface compatible with the Lambda expression.

Function interfaces provide target types for lambda expressions and method references

3. Four core functional interfaces are built into Java

Functional interface The parameter types The return type use
Consumer<T> T void Apply an operation to an object of type T, including void Accept (T T)
Supplier<T> There is no T An object of type T containing the method T get();
Function<T,R> T R Applies the operation to an object of type T and returns the result. The result is an object of type R. R apply(T T);
Predicate<T> T boolean Determines whether an object of type T satisfies a constraint and returns Boolean. Boolean test(T T);
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/* * Java8 built-in four core functional interfaces * * Consumer
      
        : void accept(T T); * Supplier
       
         : Supplier interface T get(); * Function
        
          : R apply(T T); * Predicate
         
           : Predicate type Boolean test(T T); * /
         
        ,>
       
      
public class FunctionalInterfaceTest {

    //Predicate
      
        Interface: places strings that meet conditions into the set
      
    public List<String> filterStr(List<String> list, Predicate<String> predicate) {
        List<String> newList = new ArrayList<>();
        for (String s : list) {
            if(predicate.test(s)) { newList.add(s); }}return newList;
    }

    @Test
    public void testPredicate(a) {
        List<String> list = Arrays.asList("hello"."java8"."function"."predicate");
        List<String> newList = filterStr(list, s -> s.length() > 5);
        for(String s : newList) { System.out.println(s); }}// Function
    public String strHandler(String str, Function<String, String> function) {
        return function.apply(str);
    }

    @Test
    public void testFunction(a) {
        String str1 = strHandler("Testing the built-in functional interface", s -> s.substring(2));
        System.out.println(str1);

        String str2 = strHandler("abcdefg", s -> s.toUpperCase());
        System.out.println(str2);
    }

    //Supplier
      
        Supplier interface: generates a specified number of integers and puts them into the collection
      
    public List<Integer> getNumList(int num, Supplier<Integer> supplier) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Integer n = supplier.get();
            list.add(n);
        }
        return list;
    }

    @Test
    public void testSupplier(a) {
        List<Integer> numList = getNumList(10, () - > (int) (Math.random() * 100));

        for(Integer num : numList) { System.out.println(num); }}//Consumer
      
        Consumer interface: modify parameters
      
    public void modifyValue(Integer value, Consumer<Integer> consumer) {
        consumer.accept(value);
    }

    @Test
    public void testConsumer(a) {
        modifyValue(3, s -> System.out.println(s * 3)); }}Copy the code

Many other evolution methods are provided under the Package java.util.function Package.

? > Tip

Java types are either reference types (Byte, Integer, Objuct, List) or primitive types (int, Double, Byte, char). But generics can only be bound to reference types. Converting a primitive type to the corresponding reference type is called boxing, whereas converting a reference type to the corresponding primitive type is called unboxing. Of course, Java provides an automatic boxing mechanism to do this for us.

List<Integer> list = new ArrayList();
	for (int i = 0; i < 10; i++) {
	list.add(i);    //int is boxed as Integer
}
Copy the code

But there is a cost in terms of performance. The boxed value is essentially a wrapper around the original type and stored in the heap. As a result, the boxed value requires more memory, and additional memory searches are required to retrieve the original wrapped value.

IntPredicate, DoubleConsumer, LongBinaryOperator, ToDoubleFuncation in the funciton package above are operations to avoid automatic packing. In general, the names of functional interfaces for specific input parameter types are prefixed with the corresponding primitive type.


Method reference

  • A method reference refers to a method by its name

  • Method references can be used when an operation to be passed to the Lambda body already has a method implemented. (The argument list for implementing an abstract method must match the argument list for the method referencing method!)

  • The only use of a method reference is to support the shorthand for Lambda.

Use the :: operator to separate the method name from the object or class name

1. eg

BinaryOperator<Double> binaryOperator = (x,y)->Math.pow(x,y);
/ / equivalent to the
BinaryOperator<Double> binaryOperator1 = Math::pow;
Copy the code

Method reference types

Java 8 provides four method references

Kind Example
Static method reference ContainingClass::staticMethodName
An instance method reference for a particular object containingObject::instanceMethodName
An instance method reference to any object of a particular type ContainingType::methodName
Constructor reference ClassName::new

1. Static method references

The age comparison method already exists in Person.compareByAge, so method references can be used
Arrays.sort(rosterAsArray, Person::compareByAge);
//---------------------
@Test
public void test3(a){
    BiFunction<Double,Double,Double> bif = (x,y)->Math.max(x,y);
    System.out.println(bif.apply(22.1.23.2));

    System.out.println("=== = is equivalent to ===");

    BiFunction<Double,Double,Double> bif1 = Math::max;
    System.out.println(bif1.apply(22.1.23.2));
}

@Test
public void test4(a){
    Comparator<Integer> com = (x, y)->Integer.compare(x,y);
    System.out.println(com.compare(1.2));

    System.out.println("=== = is equivalent to ===");
    Comparator<Integer> com1 = Integer::compare;
    System.out.println(com1.compare(1.2));
}
Copy the code

2. Instance method references for specific objects

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }
        
    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}
ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);
//------------------------
@Test
public void test2(a) {
    Person person = new Person("Tom", IsoChronology.INSTANCE.date(1995.6.20), Person.Sex.MALE, "[email protected]");

    Supplier<String> sup = () -> person.getName();
    System.out.println(sup.get());

    System.out.println("=== = is equivalent to ===");

    Supplier<String> sup1 = person::getName;
    System.out.println(sup1.get());
}
Copy the code

3. Instance method references of arbitrary objects of a particular type

String[] stringArray = { "Barbara"."James"."Mary"."John"."Patricia"."Robert"."Michael"."Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
//-------------------
@Test
public void test5(a){
    BiPredicate<String,String> bp = (x,y)->x.equals(y);
    System.out.println(bp.test(Java Intelligence Agency."Java Intelligence Agency 1"));
    System.out.println("=== = is equivalent to ===");

    BiPredicate<String,String> bp1 = String::equals;
    System.out.println(bp.test(Java Intelligence Agency.Java Intelligence Agency));
}
Copy the code

4. Constructor references

Copies elements in one collection into another collection.

public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>
    DEST transferElements( SOURCE sourceCollection, Supplier
       
         collectionFactory)
        {
        
        DEST result = collectionFactory.get();
        for (T t : sourceCollection) {
            result.add(t);
        }
        return result;
}
Copy the code

Supplier is a functional interface that you can call the method TransferElements using a lambda expression

Set<Person> rosterSetLambda =
    transferElements(roster, () -> { return new HashSet<>(); });
Copy the code

Use constructor references instead of lambda expressions

Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);
// The Java compiler can infer that it wants to create a HashSet containing elements of type Person, shorthand
Set<Person> rosterSet = transferElements(roster, HashSet::new);
Copy the code
Function<Integer,MyClass> fun = (n) -> new MyClass(n);
/ / equivalent to the
Function<Integer,Person> fun = MyClass::new;
BiFunction is used for a constructor reference with two arguments. For multiple arguments, you can also define one of these functional interfaces
Copy the code
@Test
public void test6(a){
    Supplier<Person> sup = ()->new Person("Tom", IsoChronology.INSTANCE.date(1995.6.20), Person.Sex.MALE, "[email protected]");
    System.out.println(sup.get());
Copy the code

Constructor references can also create arrays

@Test
public void test7(a){
    Function<Integer,String[]> fun = args -> new String[args];
    String[] strs = fun.apply(6);
    System.out.println(strs.length);
    
    System.out.println("=== = is equivalent to ===");
    
    Function<Integer,String[]> fun1 = String[]::new;
    String[] strs1 = fun1.apply(6);
    System.out.println(strs1.length);
}
Copy the code

Stream — Functional data processing

Stream is the key abstraction for dealing with collections in Java8. It can specify what you want to do with collections, and can perform very complex operations like finding, filtering, and mapping data. Manipulating collection data using the Stream API is similar to database queries executed using SQL. You can also use the Stream API to perform operations in parallel. In short, the Stream API provides an efficient and easy-to-use way to process data.

1. What is Stream

A Stream is a data channel used to manipulate sequences of elements generated by data sources (collections, arrays, and so on).

“Sets are about data, streams are about computation!”

? >tip

  • The Stream itself does not store elements

  • Stream does not change the source object. Instead, they return a new Stream holding the result

  • The Stream operation executes lazily. This means they wait until they need results

Stream operations have two important characteristics

  • Pipelining – Many stream operations themselves return a stream so that multiple operations can be linked together to form a large pipeline
  • Internal iteration — Instead of an iterator showing a collection of iterations, the iteration of a stream takes place behind the scenes

2. The operation of Stream has three steps

  1. Create Stream a data source (e.g., collection, array) and get a Stream
  2. Intermediate operations (a chain of intermediate operations that processes data from a data source to form a pipeline of flows)
  3. Termination operation (a termination operation that performs an intermediate chain of operations and produces a result)

2.1. To create a Stream

The Collection interface in Java8 has been extended to provide two methods for retrieving streams:

  • Default Stream

    Stream () : Returns a sequential Stream

  • Default Stream

    parallelStream() : returns a parallelStream

Create streams from arrays

The Java8 static stream() method of Arrays fetches array streams:

  • Static Stream Stream (T[] array): Returns a Stream

An overloaded form that can handle arrays corresponding to primitive types:

  • public static IntStream stream(int[] array)

  • public static LongStream stream(long[] array)

  • public static DoubleStream stream(double[] array)

Create a flow from a value

You can create a Stream by displaying values using the static method stream.of (). It can accept any number of parameters.

  • public static

    Stream

    of(T… Values) : Returns a stream

Create a stream from a function: Create an infinite stream

You can use the static methods stream.iterate () and stream.generate () to create an infinite Stream.

  • The iteration

    • public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
  • generate

    • public static<T> Stream<T> generate(Supplier<T> s) :
/ / create the Stream
@Test
public void test1(a){
  Collection provides two methods stream() and parallelStream().
  List<String> list = new ArrayList<>();
  Stream<String> stream = list.stream(); // Get a sequential stream
  Stream<String> parallelStream = list.parallelStream(); // Get a parallel stream

  //2. Get an array stream from stream() of Arrays
  Integer[] nums = new Integer[10];
  Stream<Integer> stream1 = Arrays.stream(nums);

  //3. Static method of() on Stream
  Stream<Integer> stream2 = Stream.of(1.2.3.4.5.6);

  //4. Create an infinite stream
  / / iteration
  Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
  stream3.forEach(System.out::println);

  / / generated
  Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
  stream4.forEach(System.out::println);
}
Copy the code

2.2. intermediate operation of Stream

Multiple intermediate operations can be joined together to form a pipeline that does not perform any processing unless termination is triggered on the pipeline! When the operation terminates, it is processed all at once, which is called lazy evaluation.

2.2.1 Screening and sectioning
methods describe
filter(Predicate p) Receive a Lambda to exclude certain elements from the stream
distinct() Filter to remove duplicate elements by hashCode() and equals() of the elements generated by the stream
limit(long maxSize) Truncate the stream so that it does not exceed a given number of elements
skip(long n) Skip the element and return a stream with the first n elements thrown away. If there are less than n elements in the stream, an empty stream is returned. And limit (n) complement each other
List<Person> persons = Person.createRoster();

// Internal iteration: Iterates through the Stream API internally
@Test
public void test2(a){
  // All intermediate operations do not do any processing
  Stream<Person> stream = persons.stream()
    .filter((e) -> {
      System.out.println("Test intermediate operation");
      return e.getAge() <= 35;
    });

  // Only when terminating an operation, all intermediate operations are executed at once, called "lazy evaluation"
  stream.forEach(System.out::println);
}

// External iteration
@Test
public void test3(a){
  Iterator<Person> it = persons.iterator();

  while(it.hasNext()){ System.out.println(it.next()); }}@Test
public void test4(a){
  persons.stream()
    .filter((p) -> {
      System.out.println("Members over 25 years of age:"); / / && | |
      return (p.getAge()) >= 25;
    }).limit(3)
    .forEach(System.out::println);
}

@Test
public void test5(a){
  persons.parallelStream()
    .filter((e) -> e.getAge() >= 20)
    .skip(2)
    .forEach(System.out::println);
}

@Test
public void test6(a){
  persons.stream()
    .distinct()
    .forEach(System.out::println);
}
Copy the code
2.2.2 mapping
methods describe
map(Function f) Take as an argument a function that is applied to each element and mapped to a new element
mapToDouble(ToDoubleFunction f) Take as an argument a function that is applied to each element, producing a new DoubleStream
mapToInt(ToIntFunction f) Receives a function as an argument that is applied to each element, producing a new IntStream.
mapToLong(ToLongFunction f) Take as an argument a function that is applied to each element, producing a new LongStream
flatMap(Function f) Take a function as an argument, replace every value in the stream with another stream, and then join all the streams into one stream
/ / map
@Test
public void test1(a){
  Stream<String> str = persons.stream()
    .map((e) -> e.getName());
  System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
  List<String> strList = Arrays.asList("aaa"."bbb"."ccc"."ddd"."eee");
  Stream<String> stream = strList.stream()
    .map(String::toUpperCase);
  stream.forEach(System.out::println);

  System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");

  Stream<Character> stream3 = strList.stream()
    .flatMap(TestStreamAPI::filterCharacter);
  stream3.forEach(System.out::println);
}

public static Stream<Character> filterCharacter(String str){
  List<Character> list = new ArrayList<>();
  for (Character ch : str.toCharArray()) {
    list.add(ch);
  }
  return list.stream();
}
Copy the code
2.2.3 sorting
methods describe
sorted() Generates a new stream, which is sorted in the natural order
sorted(Comparator comp) Produces a new stream that sorts by comparator order
@Test
public void test(a){
  persons.stream()
    .map(Person::getName)
    .sorted()
    .forEach(System.out::println);

  System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");

  persons.stream()
    .sorted((x, y) -> {
      if(x.getAge() == y.getAge()){
        return x.getName().compareTo(y.getName());
      }else{
        return Integer.compare(x.getAge(), y.getAge());
      }
    }).forEach(System.out::println);
}
Copy the code

2.3. termination of Stream

Terminal operations generate results from the pipeline of streams. The result can be any value that is not a stream, such as List, Integer, or even void

2.3.1 Search and Match
methods describe
allMatch(Predicate p) Check that all elements match
anyMatch(Predicate p) Check that at least one element matches
noneMatch(Predicate p) Check that all elements are not matched
findFirst() Returns the first element
findAny() Returns any element in the current stream
count() Returns the total number of elements in the stream
max(Comparator c) Returns the maximum value in the stream
min(Comparator c) Returns the minimum value in the stream
forEach(Consumer c) Internal iteration (Using the Collection interface requires the user to iterate, which is called external iteration. Instead, the Stream API uses internal iteration — it does the iteration for you.)
public class TestStreamAPI2 {

	List<Person> persons = Person.createRoster();	
	//3. Terminate the operation
	@Test
	public void test1(a){
			boolean bl = persons.stream()
				.allMatch((e) -> e.getGender().equals(Person.Sex.FEMALE));
			
			System.out.println("Are all members women?"+bl);
			
			boolean bl1 = persons.stream()
				.anyMatch((e) -> e.getGender().equals(Person.Sex.FEMALE));
			
			System.out.println("Are there any women among the members?"+bl1);
			
			boolean bl2 = persons.stream()
				.noneMatch((e) -> e.getGender().equals(Person.Sex.FEMALE));
			
			System.out.println("Are there no women among the members?"+bl2);
	}
	
	@Test
	public void test2(a){
		Optional<Person> op = persons.stream()
			.sorted(Comparator.comparingInt(Person::getAge))
			.findFirst();
		System.out.println("Youngest:"+op.get());
		
		Optional<Person> op2 = persons.parallelStream()
			.filter((e) -> e.getGender().equals(Person.Sex.MALE))
			.findAny();
		
		System.out.println("Any man:"+op2.get());
	}
	
	@Test
	public void test3(a){
		long count = persons.stream()
						 .filter((e) -> e.getGender().equals(Person.Sex.FEMALE))
						 .count();
		
		System.out.println("Number of girls:"+count);
		
		Optional<Integer> op = persons.stream()
			.map(Person::getAge)
			.max(Integer::compare);
		
		System.out.println("Oldest age:+op.get());
		
		Optional<Person> op2 = persons.stream()
			.min((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()));
		
		System.out.println("Youngest member:"+op2.get());
	}
	
	// Note: after a stream terminates, it cannot be used again
	@Test
	public void test4(a){
		Stream<Person> stream = persons.stream()
		 .filter((e) -> e.getGender().equals(Person.Sex.FEMALE));
		
		longcount = stream.count(); stream.map(Person::getAge) .max(Integer::compare); }}Copy the code
2.3.2 statute
methods describe
reduce(T iden, BinaryOperator b) You can combine elements in a stream repeatedly to get a value. Return T
reduce(BinaryOperator b) You can combine elements in a stream repeatedly to get a value. Returns the Optional < T >

Note: The connection between Map and Reduce is commonly known as the map-reduce mode, which is famous for Google’s use of it for web searches.

List<Person> persons = Person.createRoster();

//3. Terminate operation: reduce
@Test
public void test1(a){
  List<Integer> list = Arrays.asList(1.2.3.4.5.6.7.8.9.10);
  Integer sum = list.stream()
    .reduce(0, (x, y) -> x + y);

  System.out.println(sum);
  System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");

  Optional<Integer> op = persons.stream()
    .map(Person::getAge)
    .reduce(Integer::sum);
  System.out.println("The ages of all members and:"+op.get());
}

// Need: search for the number of occurrences of "B" in the name
@Test
public void test2(a){
  Optional<Integer> sum = persons.stream()
    .map(Person::getName)
    .flatMap(TestStreamAPI1::filterCharacter)
    .map((ch) -> {
      if(ch.equals('B'))
        return 1;
      else 
        return 0;
    }).reduce(Integer::sum);

  System.out.println(sum.get());
}
Copy the code
2.3.3 collection
methods describe
collect(Collector c) Convert the stream to another form. Receives an implementation of the Collector interface, a method for summarizing elements in a Stream

Collectors

The implementation of methods in the Collector interface determines how collection operations (such as collecting lists, sets, maps) are performed on streams. But Collectors utility class provides a lot of static methods that can easily create common collector instance, specific methods and examples in the following table: docs.oracle.com/javase/8/do…

methods The return type role The sample
toList List Collect elements from the stream into the List List list= list.stream().collect(Collectors.toList());
toSet Set Collect elements from the stream into a Set Set set= list.stream().collect(Collectors.toSet());
toCollection Collection Collect elements from the stream into the created collection Collectione mps=list.stream().collect(Collectors.toCollection(ArrayList::new));
counting Long Count the number of elements in the stream long count = list.stream().collect(Collectors.counting());
summingInt Integer Sum of integer attributes of elements in the flow Integer sum = persons.stream() .collect(Collectors.summingInt(Person::getAge));
averagingInt Double Calculates the average value of the Integer attribute of the element in the stream double avg= list.stream().collect(Collectors.averagingInt(Person::getAge));
summarizingInt IntSummaryStatistics Collect statistics for the Integer attribute in the stream. Average value IntSummaryStatistics iss= list.stream().collect(Collectors.summarizingInt(Person::getAge));
joining String Concatenate each string in the stream String str= list.stream().map(Person::getName).collect(Collectors.joining());
maxBy Optional<T> Select the maximum value based on the comparator Optionalmax= list.stream().collect(Collectors.maxBy(comparingInt(Person::getAge)));
minBy Optonal<T> Select the minimum value based on the comparator Optional min = list.stream().collect(Collectors.minBy(comparingInt(Person::getAge)));
reducing The type of reduction produced Starting with an initial value that acts as an accumulator, the BinaryOperator is combined with elements in the stream one by one to reduce to a single value int total=list.stream().collect(Collectors.reducing(0, Person::getAge, Integer::sum));
collectingAndThen Converts the type returned by the function Wrap another collector and transform the function on its results int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingBy Map<K,List<T>> The flow is grouped according to a certain attribute value, the attribute is K, and the result is V Map<Person.Sex, List<Person>> map = persons.stream() .collect(Collectors.groupingBy(Person::getGender));
partitioningBy Map<Boolean,List<T>> Partition according to true or false Map<Boolean, List<Person>> map = persons.stream() .collect(Collectors.partitioningBy((e) -> e.getAge() >= 50));
@Test
public void test3(a){
  List<String> list = persons.stream()
    .map(Person::getName)
    .collect(Collectors.toList());
  list.forEach(System.out::println);
}

@Test
public void test4(a){
  Optional<Integer> max = persons.stream()
    .map(Person::getAge)
    .collect(Collectors.maxBy(Integer::compare));

  System.out.println("Oldest age:+max.get());

  Optional<Person> op = persons.stream().min(Comparator.comparingInt(Person::getAge));

  System.out.println("Youngest member:"+op.get());

  Integer sum = persons.stream()
    .collect(Collectors.summingInt(Person::getAge));

  System.out.println("Age of all members and:"+sum);

  IntSummaryStatistics dss = persons.stream()
    .collect(Collectors.summarizingInt(Person::getAge));

  System.out.println("Oldest age:+dss.getMax());
}

/ / group
@Test
public void test5(a){
  Map<Person.Sex, List<Person>> map = persons.stream()
    .collect(Collectors.groupingBy(Person::getGender));

  System.out.println("By sex:"+map);
}

// Multilevel grouping
@Test
public void test6(a){
  Map<Person.Sex, Map<String, List<Person>>> map = persons.stream()
    .collect(Collectors.groupingBy(Person::getGender, Collectors.groupingBy((e) -> {
      if(e.getAge() >= 60)
        return "Old age";
      else if(e.getAge() >= 35)
        return "Middle-aged";
      else
        return "Adult";
    })));

  System.out.println(map);
}

/ / partition
@Test
public void test7(a){
  Map<Boolean, List<Person>> map = persons.stream()
    .collect(Collectors.partitioningBy((e) -> e.getAge() >= 50));

  System.out.println(map);
}
@Test
public void test8(a){
  String str = persons.stream()
    .map(Person::getName)
    .collect(Collectors.joining("," , "--"."--"));

  System.out.println(str);
}

@Test
public void test9(a){
  Optional<Integer> sum = persons.stream()
    .map(Person::getAge)
    .collect(Collectors.reducing(Integer::sum));
  System.out.println(sum.get());
}
Copy the code

3. Parallel and serial streams

Let’s start with parallelism and concurrency

Concurrency is when two tasks share a period of time, and parallelism is when two tasks occur at the same time, such as running on a multi-core CPU.

A parallel stream is a stream that splits a piece of content into chunks of data and uses different threads to process each chunk separately.

Java 8 is optimized for parallelism, making it easy to parallelize data. The Stream API declaratively switches between parallel and sequential streams using parallel() and sequential(). If you want to create a stream from a collection class, call parallerStream to get a parallel stream.

public static long parallelSum(long n) {
    return Stream.iterate(1L, i -> i + 1)
        .limit(n)
        .parallel()    // Convert the flow to a parallel flow
        .reduce(0L, Long::sum);
}
Copy the code

Configure the thread pool used by the parallel flow

Using the parallel approach to streams, you might be wondering, where does the thread for the parallel stream come from? How many? How to customize?

Internal parallel streams use a default ForkJoinPool. The default number of threads is the number of your Processors. This value is given by Runtime.getrRuntime().acailable-Processors().

You can use the system property java.util.concurrent.ForkJoinPool.com mon. Parallelism to change the thread pool size, are as follows

System.setProperty(“java.util.concurrent.ForkJoinPool.common.parallelism”,”12″); This is a global setting, and therefore affects all parallel streams in your code. (Currently, it is not possible to specify this value specifically for a parallel stream; in general, a ForkJoinPool size equal to the number of processors is a good default.)

Efficient use of parallel streams

  • Parallel flows are not always faster than sequential flows
  • Pay attention to packing. Automatic boxing and unboxing can significantly degrade performance, and Java8 has streams of primitive type (IntStream, LongStream…) To avoid this operation
  • Some operations themselves perform worse on a parallel stream than a sequential stream, especially operations such as Limit and findFirst that depend on the order of elements, which can be expensive to execute on a parallel stream
  • Also consider the total computational cost of the flow’s pipeline of operations
  • For small volumes of data, there is no need to use parallel streams
  • Consider whether the data structure behind the stream is easily decomposed; for example, ArrayList is much more efficient at splitting than LinkedList, which requires no traversal
  • Also consider the cost of merging steps in terminal operations (such as the combiner method in the Collector)

4. The Fork/Join framework

The basic framework used behind parallel flows is the branch/merge framework introduced in Java7.

The purpose of the Fork/Join framework is to recursively Fork tasks that can be parallel into smaller tasks, and then Join the results of each task to produce an overall effect. It is an implementation of the ExectorService interface that assigns subtasks to worker threads in a thread pool called ForkJoinPool.

Fork/Join framework: it is to Fork a large task into several small tasks (when they are undeassembled) when necessary, and then Join together the results of each small task operation

// Use the branch/merge framework to sum in parallel
public class ForkJoinSumCalculator extends RecursiveTask<Long> {

    private final long[] numbers;
    private final int start;
    private final int end;

    // The task is no longer decomposed into the array size of the subtasks
    public static long THRESHOLD = 100;

    // The public constructor is used to create the main task
    public ForkJoinSumCalculator(long[] numbers) {
        this(numbers, 0, numbers.length);
    }

    Private constructors are used to recursively create subtasks for the main task
    private ForkJoinSumCalculator(long[] numbers, int start, int end) {
        this.numbers = numbers;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute(a) {
        int length = end - start;
        // If the size is less than or equal to the threshold, compute the results in order
        if (length <= THRESHOLD) {
            return computerSequntially();
        }

        ForkJoinSumCalculator leftTask = new ForkJoinSumCalculator(numbers, start, start + length / 2);

        leftTask.fork();

        ForkJoinSumCalculator rightTask = new ForkJoinSumCalculator(numbers, start + length / 2, end);

        Long rightResult = rightTask.compute();   // Execute the second task synchronously,
        Long leftResult = leftTask.join(); // Read the result of the first subtask and wait if it has not been completed
        return rightResult + leftResult;
    }


    // Subtasks can no longer be calculated and divided in time
    private long computerSequntially(a) {
        long sum = 0;
        for (int i = start; i < end; i++) {
            sum += numbers[i];
        }
        return sum;
    }

    public static long forkJoimSum(long n) {
        long[] numbers = LongStream.rangeClosed(1, n).toArray();
        ForkJoinTask<Long> task = new ForkJoinSumCalculator(numbers);
        return new ForkJoinPool().invoke(task);
    }

    public static void main(String[] args) {
        System.out.println("sum:" + forkJoimSum(10000)); }}Copy the code

Differences between Fork/Join frameworks and traditional thread pools

It uses work-stealing: When executing a new task, it can break it into smaller tasks and add the smaller task to the thread queue. It can then steal one from a random thread’s queue and place it in its own queue.

The advantage of the fork/join framework over the normal thread pool implementation lies in the way it handles the contained tasks. In the normal thread pool, if a thread is executing a task that cannot continue for some reason, the thread will be in a wait state, whereas in the fork/ Join framework implementation, If a subproblem cannot continue running because it is waiting for the completion of another subproblem, the thread handling that subproblem will actively look for other subproblems that have not yet been run to execute, which reduces the wait time of the thread and improves performance.

Best practices using the Fork/Join framework

  • Calling the Join method on a task blocks the caller until the task produces a result. Therefore, it is necessary to call it after the computation of both subtasks has begun
  • ForkJoinPool invoke methods should not be used inside a RecursiveTask. Instead, you should always call compute or fork directly, and only sequential code should use invoke to start parallel computation

Work stealing

5. Spliterator

“Separable iterators” — Spliterator, like Iterator, is used to iterate over elements in a data source and is designed for parallel execution.

Java8 provides a default Spliterator method for all data structures contained in the collections framework. Collections implement the Spliterator interface, which provides a Spliterator method.

Default and static methods in interfaces

Traditionally, a class in Java that implements an interface must provide an implementation class for each method defined in the interface, or inherit its implementation from a parent class. This approach can be problematic if the library designer needs to modify the interface to include new methods. All entity classes that use this interface will need to be modified to fit the new interface convention (if they are so incompatible, they will be obsolete sooner or later). So Java8 introduces a new mechanism to address this problem. Interfaces in Java8 support providing implementations while declaring methods. First, Java8 allows static methods to be declared in interfaces. Second, Java8 introduced a new feature called default methods that specify the default implementation of interface methods (so classes that implement interfaces that do not explicitly provide an implementation of that method automatically inherit the default implementation, which allows you to smooth interface optimization and upgrade).

The default method

Java 8 allows interfaces to include methods with concrete implementations called “default methods,” which are decorated with the default keyword.

interface MyFunc<T>{
    T func(int a);

    default String getName(a){
        return "hello java8"; }}Copy the code
@Test
public void test1(a){
    List<Integer> list = Arrays.asList(22.11.33.55.4);
    //sort is the default method in the List interface. NaturalOrder is the static method of the Comparator
    list.sort(Comparator.naturalOrder());
    for(Integer integer : list) { System.out.println(integer); }}Copy the code

The default method is class-first

If a default method is defined in one interface and a method with the same name is defined in another parent class or interface

  • Select the method in the parent class. If a parent class provides a concrete implementation, default methods with the same name and parameters in the interface are ignored.

  • The interface conflicts. Procedure If one parent interface provides a default method and another interface provides a method with the same name and argument list (whether or not the method is the default), then that method must be overridden to resolve the conflict

interface MyFunc<T> {
    default String getName(a) {
        return "hello java8"; }}interface MyFunc1 {
    default String getName(a) {
        return Hello Java Intelligence Bureau; }}class MyClass implements MyFunc.MyFunc1 {

    @Override
    public String getName(a) {
        return MyFunc1.super.getName(); }}Copy the code

The JavaAPI designers take full advantage of the default methods and add many new methods to the collection interface and classes.


Six, Optional classes

1. Replace null with Optional

Isn’t your first impulse when you encounter a NullPointerException in your application to go to the code, add an if statement, and check?

NullPointerException is a typical exception in Java program development. To avoid such exceptions, our code might be riddled with layer after layer of deeply nested NULL checks, making the code very unreadable.

The Optional class (java.util.Optional) is a container class that represents the presence or absence of a value. Null-pointer exceptions can also be avoided.

Knowledge of the Optional class simply encapsulates the class when the variable exists. When the variable does not exist, the missing value is modeled as an “empty” Optional object returned by the option.empty () method.

Common methods:

  • Option. of(T T) : Creates an Optional instance
  • Option.empty () : Creates an empty Optional instance
  • Option. ofNullable(T T): If T is not null, create an Optional instance; otherwise, create an empty instance
  • IsPresent () : checks whether a value is included. OrElse (T T) : Returns a value if the calling object contains it, T otherwise
  • OrElseGet (Supplier s) : Returns the value if the calling object contains it, otherwise returns the value obtained by S
  • Map (Function f): if it has a value, return the processed Optional, otherwise return option.empty ()
  • FlatMap (Function mapper): Similar to map, the return value must be Optional

2. Optional

2.1 Creating Optional Objects

@Test
public void test(a){

    Optional<Person> optional = Optional.empty();  // Create an empty Optional

    Optional<Person> op = Optional.of(new Person());
    Person p = op.get();
    System.out.println(p);   //Person{name='null', birthday=null, gender=null, emailAddress='null'}

    Person person = null;
    Optional<Person> op1 = Optional.of(person); // Person is null, NullPointerException is thrown

    Optional<Person> op2 = Optional.ofNullable(person);   // Create Optional objects that allow NULL

}
Copy the code

2.2 Optional Object Operations

@Test
public void test4(a){
    Person person = new Person("Tom",IsoChronology.INSTANCE.date(1999.7.15),Person.Sex.FEMALE, "[email protected]")
    Optional<Person> op = Optional.ofNullable(person);

    Optional<String> op1 = op.map(Person::getName);
    System.out.println(op1.get());
    
    /** * Extract and transform values from optional objects using map * If you want to extract people's names, you need to judge persion before! =null,Optional provides a map method that handles **/
    Optional<String> op2 = op.map(Person::getName);
    System.out.println(op2.get());

    // Use flatMap to link optional objects
    Optional<String> op3 = op.flatMap((e) -> Optional.of(e.getName()));
    System.out.println(op3.get());
    
    //TODO
}
Copy the code

CompletableFuture — Combinative asynchronous programming

1. The Future interface

The Future interface was introduced in Java 5 and was designed to model outcomes that will happen at some point in the Future. It models an asynchronous computation that returns a reference to the result of an operation, which is returned to the caller when the operation is complete. Triggering potentially time-consuming operations in the Future frees up the calling thread to continue performing other valuable work without waiting for time-consuming operations to complete. For example, you can imagine this scenario: You take a bag of clothes to your favorite dry cleaner. The dry cleaner will give you an invoice telling you when your clothes will be ready (this is a Future event). You can do something else while your clothes are dry-cleaned. Another advantage of a Future is that it is easier to use than the lower-level Thread. To use a Future, you usually just encapsulate time-consuming operations in a Callable object and submit it to the ExecutorService. The following code shows an example of Future use prior to Java 8.

ExecutorService executor = Executors.newCachedThreadPool();
Future<Double> future = executor.submit(new Callable<Double>() {
    public Double call(a) {
        return doSomeThings();    // Perform operations asynchronously in a new thread}});//doSomethingElse(); // While asynchronous operations are going on, other things can be done
try {
    // Get the result of the asynchronous operation, if blocked, wait 1 second and exit
    Double result = future.get(1, TimeUnit.SECONDS);   
} catch (ExecutionException | InterruptedException | TimeoutException e) {
}
Copy the code

1.1 Limitations of the Future interface

While Future and related usage methods provide the ability to execute tasks asynchronously, it is very inconvenient to obtain results, which can only be obtained through blocking or polling. Blocking works against the purpose of asynchronous programming, polling uses unnecessary CPU resources, and doesn’t get results in time. Why not use observer design to notify listeners when the results are complete?

Some Java frameworks, such as Netty, extend Java’s Future interface by themselves, providing addListener and other extension methods. Google Guava also provides generic extension Futures :ListenableFuture, SettableFuture, and helper Futures to facilitate asynchronous programming.

As an orthodox Java class library, should do something to enhance the functionality of its own library?

In Java 8, a new class containing around 50 methods was added: CompletableFuture, which offers very powerful extensions to Future, helps simplify the complexity of asynchronous programming, provides the ability to do functional programming, process the results of calculations through callbacks, and provides methods to transform and combine CompletableFuture.

For example, implement the following examples:

  • Merge two asynchronous computations into one — the two asynchronous computations are independent of each other, while the second depends on the result of the first
  • Wait for all tasks in the Future collection to complete
  • Just wait for the fastest finished task in the Future collection to complete (possibly because they are trying to compute the same value in different ways) and return its result
  • Perform a Future task programmatically (that is, manually set the result of an asynchronous operation)
  • To handle Future completion events (that is, to be notified when a Future’s completion event occurs and to be able to use the result of the Future calculation for further actions, rather than simply blocking and waiting for the result of the operation)

1.2 Use CompletableFuture to build an asynchronous application

public class TestCompletableFuture {
    public static CompletableFuture<Integer> compute(a) {
        final CompletableFuture<Integer> future = new CompletableFuture<>();
        return future;
    }
    public static void main(String[] args) throws Exception {
        final CompletableFuture<Integer> f = compute();
        class Client extends Thread {
            CompletableFuture<Integer> f;
            Client(String threadName, CompletableFuture<Integer> f) {
                super(threadName);
                this.f = f;
            }
            @Override
            public void run(a) {
                try {
                    System.out.println(this.getName() + ":" + f.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch(ExecutionException e) { e.printStackTrace(); }}}new Client("Client1", f).start();
        new Client("Client2", f).start();
        System.out.println("waiting");
        f.complete(100); System.in.read(); }}Copy the code

New time and date API

1. Use LocalDate, LocalTime, and LocalDateTime

  • Instances of the LocalDate, LocalTime, and LocalDateTime classes are immutable objects that represent the date, time, date, and time using the ISO-8601 calendar system, respectively. They provide a simple date or time and do not contain current time information. Nor does it contain time zone information.

    @Test
    public void test1(a){
        LocalDate date = LocalDate.of(2020.01.03);
        Month month = date.getMonth();
        System.out.println(month);    //JANUARY
    
        DayOfWeek dayOfWeek = date.getDayOfWeek();
        System.out.println(dayOfWeek);   //FRIDAY
    
        int len = date.lengthOfMonth();
        System.out.println(len);  / / 31
        // Use TemporalField(ChronoField enumeration implements this interface) to read the value of LocalDate
        int year = date.get(ChronoField.YEAR);
        System.out.println(year);  / / 2020
    
        LocalDate ld = LocalDate.parse("2020-01-03");
        System.out.println(ld);   / / 2020-01-03
    
        LocalTime time = LocalTime.of(19.56.11);
        System.out.println(time);  / / 19:56:11
    
        LocalDateTime ldt = LocalDateTime.now();
        LocalDateTime l1 = LocalDateTime.of(2020.01.03.18.48);
        System.out.println(l1);  //2020-01-03T18:48
    
        LocalDateTime l2 = l1.plusYears(3);
        System.out.println(l2);     //2023-01-03T18:48
    
        LocalDateTime l3 = l1.minusMonths(1);
        System.out.println(l3);  //2019-12-03T18:48
        System.out.println(l3.getMinute()+","+l3.getYear());   / / 48201
    }
    Copy the code

2. Instant timestamp

  • Time stamp operation. It is calculated as the number of seconds that have elapsed since the first year of Unix (traditionally set at midnight on January 1, 1970, in the UTC time zone)

    @Test
    public void test2(a){
        Instant ins = Instant.now();  // The default UTC time zone is used
        OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);
        System.out.println(ins.getNano());
        Instant ins2 = Instant.ofEpochSecond(5);
        System.out.println(ins2);
    }
    Copy the code

3. Duration and Period

  • Duration: Used to calculate two “time” intervals

  • Period: Used to calculate two “date” intervals

    @Test
    public void test3(a){
        Instant ins1 = Instant.now();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        Instant ins2 = Instant.now();
        System.out.println("The time spent is:" + Duration.between(ins1, ins2));   
    
        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -");
        LocalDate ld1 = LocalDate.now();
        LocalDate ld2 = LocalDate.of(2019.1.1);
    
        Period pe = Period.between(ld2, ld1);
        System.out.println(pe.getYears());  
        System.out.println(pe.getMonths());
        System.out.println(pe.getDays());
    }
    Copy the code

4. Date manipulation

  • Modify the LocalDate property with the withXXX method

  • TemporalAdjuster: Time adjuster. Sometimes we may need to get actions such as: change the date to “next Sunday”.

  • TemporalAdjusters: This class provides a substantial implementation of the commonly used TemporalAdjuster through a static approach.

    @Test
    public void test(a){
        LocalDate date = LocalDate.now();
        // Modify the LocalDate attribute with the withAttributer method
        LocalDate date1 = date.with(ChronoField.ALIGNED_WEEK_OF_YEAR,9);
        LocalDate date2 = date.withYear(2019);
        LocalDate date3 = date.withDayOfMonth(11);  // Change the number to 11
        System.out.println(date1);
        / / next Sunday
        LocalDate nextSunday = LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(nextSunday);
    }
    Copy the code

5. Parse and format

Java. Time. The format. DateTimeFormatter class: this class format provides three methods:

  • Predefined standard formats

  • Locale-specific formats

  • Custom format

    @Test
    public void test(a){
        //DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("Yyyy: MM :ss E");
    
        LocalDateTime ldt = LocalDateTime.now();
        String strDate = ldt.format(dtf);
    
        System.out.println(strDate); // Friday, 03 January 2020 20:32:14
    
        LocalDateTime newLdt = ldt.parse(strDate, dtf);
        System.out.println(newLdt);  //2020-01-03T20:32:14
    }
    Copy the code

Time Zone handling

  • Java8 supports time zones. The time zones are ZonedDate, ZonedTime, and ZonedDateTime

    Each time zone has an ID. The region ID is in the format of {region}/{city}, for example, Asia/Shanghai

    ZoneId: This class contains all time zone information

    • GetAvailableZoneIds () : obtains time zone information of all time zones
  • Of (id) : Gets the ZoneId object with the specified time zone information

    @Test
    public void test(a){
        Set<String> set = ZoneId.getAvailableZoneIds();  // run
        set.forEach(System.out::println);
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println(ldt);
    
        ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
        System.out.println(zdt);
    }
    Copy the code

Repeat annotations and type annotations

annotations

Java 8 offers two improvements to annotation handling: repeatable annotations and annotations that can be used for types

In Java, annotations are a mechanism for configuring program elements to provide additional information. (Prior to Java8, annotations were only used for declarations.)

Repeated notes

Java8 previously did not allow repeated comments like this, so it used a few common practices to get around this limitation. You can declare a new annotation that contains an array of annotations that you want to repeat.

Create a repeat annotation

  1. Mark the annotation as ** @REPEATable **
  2. Provides a container for annotations
import java.lang.annotation.Repeatable;
@Repeatable(Authors.class)
public @interface Author {
    String name(a);
}
Copy the code
public @interface Authors {
    Author[] value();
}
Copy the code
@Author(name = "Java")
@Author(name = "Android")
public class Book {
    public static void main(String[] args) { Author[] authors = Book.class.getAnnotationsByType(Author.class); Arrays.asList(authors).forEach(s->{ System.out.println(s.name()); }); }}Copy the code

Type annotations

As of Java8, annotations can be applied to any type. Includes the new operator, type conversions, instanceof checks, generic type parameters, and implemtnTs and throws clauses.

@NotNull String name = person.getName();    //getName does not return null

List<@NotNull Person> persons = new ArrayList<>();  //persons is always non-empty
Copy the code

X. Other language features

Atomic operation

Java. Util. Concurrent. Atomic package provides a number of digital type operation, such as AtomicInteger and AtomicLong, they support for a single atomic operation variables. These classes have added more method support in Java 8.

  • GetAndUpdate – Updates the current value atomically with the given method and returns the value before the change

  • UpdateAndGet – Atomically updates the current value with the given method and returns the changed value

  • GetAndAccumulate — updates the current and given values atomically with the given method and returns the value before the change

  • AccumulateAndGet — Atomically updates the current and given value with the given method and returns the changed value

Adder and Accumulator

In a multithreaded environment, if multiple threads need to update frequently and there is little read activity (for example, in the context of statistical calculations), The Java API documentation recommends using the new classes LongAdder, LongAccumulator, double-Adder, and DoubleAccumulator as much as possible, avoiding their corresponding atomic types. These new classes are designed with dynamically growing requirements in mind and can effectively reduce contention between threads.

Both the LongAddr and DoubleAdder classes support addition, while LongAccumulator and DoubleAccumulator can combine multiple values using a given method.

ConcurrentHashMap

The introduction of the ConcurrentHashMap class has greatly improved the modernization of HashMap, and the newly introduced ConcurrentHashMap is very concurrency friendly. ConcurrentHashMap allows concurrent additions and updates because it locks only certain parts of the internal data structure. Therefore, it provides better read and write performance than the alternative, synchronous Hashtable.

  1. performance

    To improve performance, the internal data structure of ConcurrentHashMap is adjusted. Typically, map entries are stored in buckets and accessed based on key-generated hashes. However, performance deteriorates if a large number of keys return the same hash value, and since buckets are implemented by lists, their query complexity is O(n). In Java 8, when buckets become too bloated, they are dynamically replaced with a sorted tree, a new data structure with better query performance (the sorted tree has O(log(n) query complexity)). Note that this optimization is only possible if the key is comparable (such as the String or Number class).

  2. Such flow operation

    ConcurrentHashMap supports three new operations that are similar to those you’ve seen in the stream before:

  • ForEach – Specific operations are performed on each key-value pair

  • Reduce — Combines all the key-value pairs into a result 􏰝 using the given 􏰤 reduction function

  • Search – Executes a function on each key-value pair until the function returns a non-null value

    Each of these operations supports four forms and accepts functions that use keys, values, map.entry, and key-value pairs:

  • Operations using keys and values (forEach, reduce, search)

  • Key operations (forEachKey, reduceKeys, searchKeys)

  • Operations using values (forEachValue, reduceValues, searchValues)

  • Operations using map. Entry objects (forEachEntry, reduceEntries, searchEntries)

Note that these operations do not lock the state of ConcurrentHashMap. They only operate on elements at run time. The functions applied to these operations should not depend on any order, or other objects, or values that change during the computation. In addition, you need to specify a concurrency threshold for these operations. If the estimated map size is smaller than the threshold, the operations are performed in sequence. Use the value 1 to turn on maximum parallelism based on the common thread pool. Use the value long.max_value to set the program to perform operations in a single thread. In the following example, we use reduceValues to try to find the maximum value in the map:

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(); 
Optional<Integer> maxValue = Optional.of(map.reduceValues(1, Integer::max));
Copy the code

Note that for int, long, and double, they have different reduce operations (such as reduceValuesToInt, reduceKeysToLong, etc.).

  1. count

    The ConcurrentHashMap class provides a new method called mappingCount that returns the number of mappings in the map as a long integer. We should try to use this new method instead of the old size method, which returns type int. This is because there may be a number of mappings that int cannot represent.

  2. Collection view

    The ConcurrentHashMap class also provides a new method called KeySet that returns a view of the ConcurrentHashMap in the form of a Set (changes to the map are reflected in that Set and vice versa). You can also use the new static method newKeySet, which creates a Set from ConcurrentHashMap.

Arrays

The Arrays class provides different static methods for manipulating Arrays. It now includes four new methods (all of which have specially overloaded variables)

  • ParallelSort: The parallelSort method sorts the specified arrays concurrently, either using natural order or using the parallelSort method

    Define special comparators for array objects

  • SetAll and parallelSetAll: the setAll and parallelSetAll methods can be used sequentially or concurrently, using the provided functions to evaluate each element and setAll elements in the specified array

  • ParallelPrefix: The parallelPrefix method works concurrently with user-supplied binary operators for each element in a given array

    Perform cumulative calculations

Number

New method in Number class

  • The Short, Integer, Long, Float, and Double classes provide static methods sum, min, and Max
  • The Integer and Long classes provide the compareUnsigned, divideUnsigned, remainderUnsigned, and toUnsignedLong methods to handle unsigned numbers.
  • The Integer and Long classes also provide static methods parseUnsignedInt and parseUnsignedLong to parse a character into an unsigned int or Long, respectively.
  • The Byte and Short classes provide the toUnsignedInt and toUnsignedLong methods to convert arguments to int or long via unsigned conversions. Similarly, the Integer class now provides the static method toUnsignedLong.
  • The Double and Float classes provide a static method, isFinite, to check whether the argument is a finite floating point number.
  • The Boolean class now provides static methods logicalAnd, logicalOr, and logicalXor, which can perform AND, OR, and xOR operations between the two Boolean classes.
  • The BigInteger class provides byteValueExact, shortValueExact, intValueExact, and longValueExact. The BigInteger class converts the value of the BigInteger type to the corresponding base type. However, if information is lost during the transformation, the method throws an arithmetic exception.

Math

The Math class provides new methods to throw arithmetic exceptions if methods in Math appear ຼ in an operation. Methods that support this exception include addExact, subtractExact, multipleExact, incrementExact, decrementExact, and negateExact using the int and long parameters. In addition, the Math class has added a static method, toIntExact, that converts long values to int values. Other new additions include static methods floorMod, floorDiv, and nextDown.

Files

The most notable change to the Files class is that you can now generate streams directly from Files

  • Files.list — Generates a Stream consisting of all entries in the specified directory. This list is not recursively contained. Because streams are lazily consumed, this approach is useful when dealing with very large directories
  • Files.walk — Similar to files.list, it also generates a Stream containing all the entries in a given directory. But the list is recursive, and you can set the depth of the recursion. Note that this traversal is depth-first
  • Files.find — Recursively traverse a directory to find qualifying entries and generate a Stream object

String

The String class also added a static method called 􏱗 called Join. It can concatenate multiple strings 􏶘 with a delimiter. The same as stringutils.join provided by Apache that we used before.

Reflection

The Reflection API changes were made to support the annotation mechanism in Java 8. In addition, the Relection interface of another change is that added can query API method Parameter information, for example, you can now use the new Java lang. Reflect. The Parameter query method Parameter names and modifier.


FAQ

  • What new features do you know about Java8?

  • What is a lambda expression? What are the advantages?

  • Is ConcurrentHashMap implemented differently in Java8 and Java7?

  • Can you tell us about the improved JVM in Java 8?

  • What changes have been made to the hashMap principle and java8?

  • External iteration and internal iteration, you know?


reference

Java 8 In Action

Java 8 Functional Programming

Java 8 official documentation

A free video learning site