java8 is a new feature. I learned it a long time ago. I wrote notes at that time and uploaded them for backup. After my blog goes online, it will be removed. It's not very difficult to understand. It's much easier to understand than c++11.
Lambda, optional and streamApi are combined together. Optional doesn't write much. It's only necessary to understand lambda and streamApi.
Lambda
Lambda is an anonymous function. We can understand a lambda expression as a piece of code that can be passed (pass the code like data). Using it, you can write more concise and flexible code. As a more compact code style, the language ability of Java has been improved.
- Lambda demonstration:
@Test public void test02(){ Comparator<Integer> comparable = new Comparator<Integer>() { @Override public int compare(Integer o1,Integer o2) { return Integer.compare(o1,o2); } }; int compare = comparable.compare(1, 2); System.out.println(compare); //lambda expressions Comparator<Integer> comparator = (o1,o2) -> Integer.compare(o1,o2); int compare1 = comparator.compare(21, 20); System.out.println(compare1); //Method reference Comparator<Integer> comparator1 = Integer::compare; int compare2 = comparator.compare(21, 20); System.out.println(compare2); }
Lambda format:
->Is a lambda operator or an arrow operator.
On the left of the arrow is the formal parameter list (actually the parameter list of the abstract method in the interface)
To the right of the arrow is the lambda body (actually the method body of the overridden abstract method)
The essence of lambda expressions is as instances of functional interfaces.
lambda can be used in 6 cases:
- No parameter, no return value
@Test public void test01(){ Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello world"); } }; r.run(); Runnable r2 = () -> System.out.println("Hello!"); r2.run(); }
- There is one parameter but no return value
@Test public void test02(){ Consumer<String> con = new Consumer<String>() { @Override public void accept(String s) { System.out.println(s); } }; con.accept("Hello World!"); Consumer<String> con1 = (String s) -> { System.out.println(s); }; con1.accept("Hello World!"); }
- Data types can be omitted because they can be inferred by the compiler, which is called "type inference"
@Test public void Test03(){ //Type inference Consumer<String> con = (s) -> { System.out.println(s); }; con.accept("Hello World!"); }
- If lambda requires only one parameter, the left parenthesis can also be omitted
@Test public void Test04(){ Consumer<String> con = s -> { System.out.println(s); }; con.accept("Hello World!"); }
- When a lambda needs two or more parameters, it can execute multiple statements and can have return values
@Test public void Test05(){ Comparator<Integer> com = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); } }; int compare = com.compare(12, 21); System.out.println(compare); Comparator<Integer> com1 = (o1,o2)->{ System.out.println(o1); System.out.println(o2); return o1.compareTo(o2); }; int compare1 = com1.compare(6, 5); System.out.println(compare1); }
- When there is only one statement in the lambda body, return and braces, if any, can be omitted
@Test public void Test06(){ Comparator<Integer> com1 = (o1,o2)->{ return o1.compareTo(o2); }; System.out.println(com1.compare(12,21)); Comparator<Integer> com2 = (o1,o2)->o1.compareTo(o2); System.out.println(com2.compare(6,5)); }
- Summary:
- On the left: the parameter type of the lambda parameter list can be omitted (type inference); if the lambda parameter list has only one parameter, the parentheses can also be omitted.
- Right: the lambda body should be wrapped with a pair of braces, but if there is only one execution statement, you can omit the braces and the return keyword.
Functional interface
If only one abstract method is declared in an interface, the interface is called a functional interface
You can create objects of interfaces through lambda expressions. (if a lambda expression throws a checked exception, i.e. a runtime exception, the modified exception needs to be declared on the abstract method of the target interface.)
We can use the @ FunctionalInterface annotation on an interface to check whether it is a function interface. At the same time, javadoc will also contain such a declaration that this interface is a functional interface.
java. util. The function package defines the rich functional interfaces of Java 8
Four core interfaces
Functional interface | Parameter type | Return type | purpose |
---|---|---|---|
Consumer<T> Consumer interface | T | void | Apply operations to objects of type T, including methods: void accept (T) |
Supplier<T> Supply type interface | nothing | T | Returns an object of type T, including the method: T get() |
Function<T,R> Functional interface | T | R | Applies an operation to an object of type T and returns a result. The result is an object of type R. Include Method R apply (T) |
Predicate<T> Deterministic function | T | boolean | Determines whether an object of type T satisfies a convention and returns a boolean value. Include method boolean test (T) |
@Test public void Test08(){ happyTime(500,money-> System.out.println("spend: "+money)); //HappyTime (500, (double money) - > {system. Out. Println ("cost:" + money);}); List<String> list = Arrays.asList("Beijing","Tianjin","Tokyo","the Western Capital","Vladimir Putin"); List<String> list1 = filterString(list, s -> s.contains("Beijing"));//Take out all data containing "Jing" //List < string > List2 = filterstring (list, (string s) - > {return s.contains ("Jing");}); System.out.println(list1); } public void happyTime(double money,Consumer<Double> consumer){ consumer.accept(money); } public List<String> filterString(List<String> list, Predicate<String> predicate){ List<String> arrayList = new ArrayList<>(); for (String s:list){ if (predicate.test(s)){ arrayList.add(s); } } return arrayList; }
Other interfaces
Functional interface | Parameter type | Return type | purpose |
---|---|---|---|
BiFunction<T,U,R> | T,U | R | Apply operations to parameters of type T and u and return results of type R. Including methods: R apply (T, t, U); |
UnaryOperator<T> (Function sub interface) | T | T | Performs a unary operation on an object of type T and returns the result of type T. Include method t apply |
BinaryOperator<T> (BiFunction sub interface) | T,T | T | Performs a binary operation on an object of type T and returns the result of type T. The included objects are T apply(T t1,T t2); |
BiConsumer<T,U> | T,U | void | Apply operations to parameters of type T, U. Including methods: void accept (T, t, u, U) |
BiPredicate<T,U> | T,U | boolean | Including methods: Boolean test (T, t, u, U) |
ToIntFunction<T> ToLongFunction<T> ToDoubleFunction<T> | T | int long double | Functions that calculate the values of int, long and double respectively |
IntFunction<R> LongFunction<R> DoubleFunction<R> | int long double | R | The parameters are functions of type int, long and double respectively |
Method reference
When the operation to be passed to the lambda body already has an implementation method, you can use the method reference.
Method reference can be regarded as the deep expression of lambda expression. In other words, a method reference is a lambda expression, that is, an instance of a functional interface. A method is guided by its name, which can be considered as a syntax sugar of a lambda expression.
Requirement: the parameter list and return value type of the abstract method implementing the interface must be consistent with the parameter list and return value type of the reference method.
Format: use:: to separate (class) or object from method name.
There are three situations:
- Object:: instance method name
- Class:: static method name
- Class:: instance method name
public class Test02 { public static void main(String[] args) { Consumer<String> consumer = System.out::println; consumer.accept("Hello"); Function<Double,Long> function = new Function<Double, Long>() { @Override public Long apply(Double aDouble) { return Math.round(aDouble); } }; Function<Double,Long> function1 = aDouble -> Math.round(aDouble); Function<Double,Long> function2 = Math::round; Long apply = function2.apply(1.2); System.out.println(apply); Comparator<String> comparator = (s1,s2)->s1.compareTo(s2); Comparator<String> comparator1 = String::compareTo; int compare = comparator1.compare("abc", "abd"); System.out.println(compare); } }
Optional class
So far, the notorious null pointer exception is the most common cause of failure of Java applications. In order to solve the null pointer exception, the Optional class was modified and introduced. Now the Optional class and a part of the Java8 class library.
The optional < T > class (java.util.Optional) is a container class that can store the value of type T, representing that the value exists. Or just save null, indicating that the value does not exist. Originally, null was used to indicate that a value does not exist. Now optional can better express this concept. And you can avoid null pointer exceptions.
javadoc description of Optional class: This is a null able container object. If the value exists, the isPresent() method returns true, and calling the get() method returns the object.
StreamAPI
One of the most important changes in Java 8 is the lambda expression, and the other is the stream API.
The Stream API(java.util.stream) introduces a true functional programming style into Java. This is the best supplement to the Java library so far, because the Stream API can greatly improve the productivity of Java programmers and enable programmers to write more efficient, clean and concise code.
Stream is a key abstraction for dealing with collections in Java 8. It can specify the operations you want to perform on the collection, and can perform very complex operations such as finding, filtering and mapping data. Using the Stream API to operate on a collection data process is similar to using SQL to query a database. You can also use the Stream API to perform parallel operations. In short, the Stream API provides a funny and easy-to-use way to process data.
Why use the Stream API
In the actual development, a lot of data in the project comes from Mysql, Oracle and so on. But now there are more data sources, such as MongDB and Redis, and these NoSQL data need to be processed at the Java level.
The difference between Stream and Collection: Collection is a static memory data structure, while Stream is about calculation. The former is mainly memory oriented and stored in memory, while the latter is mainly CPU oriented and realizes computing through CPU.
Stream is a data channel, which is used to manipulate the element sequence generated by the data source (set, array, etc.). "Set is about data, stream is about calculation".
be careful:
- Stream does not store its own elements.
- Stream does not change the source object. Instead, they return a new stream that holds the result.
- Stream operation is executed with delay. This means that they wait until the results are needed.
Three steps of Stream operation
-
Create Stream
A data source (such as collection and array) to obtain a stream. -
Intermediate operation
An intermediate operation chain that processes the data of the data source. -
Terminate operation (terminal operation)
Once the termination operation is performed, the intermediate operation chain is executed and the results are generated. After that, it will not be called again.

Stream construction method
- Custom Person class
@Data @AllArgsConstructor @NoArgsConstructor//Lombok plug-in can be imported from maven @ToString public class Person { private long id; private String name; private int age; }
- Four construction methods of Stream
public class Test01 { public List<Person> getPersonList(){ List<Person> list = new ArrayList<>(); list.add(new Person(1,"user1",12)); list.add(new Person(2,"user2",13)); list.add(new Person(3,"user3",14)); list.add(new Person(4,"user4",15)); list.add(new Person(5,"user5",12)); list.add(new Person(6,"user6",12)); list.add(new Person(6,"user6",12)); return list; } @Test public void test01() { //Method 1 of creating Stream: through collection List<Person> personList = getPersonList(); Stream<Person> stream = personList.stream();//Default stream < E > stream() returns a sequential stream Stream<Person> personStream = personList.parallelStream();//Default stream < E > parallelstream() returns a parallel stream //Method 2 of creating Stream: through array int[] arr = new int[]{1,2,3,4}; IntStream stream1 = Arrays.stream(arr);//Call static < T > stream < T > stream (t [] array) of Arrays class to return a stream Person[] people = new Person[]{new Person(1,"hello1",1),new Person(2,"hello2",3)}; Stream<Person> personStream1 = Arrays.stream(people); //Method 3 for creating a Stream: pass the of() of the Stream Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6); Stream<Person> personStream2 = Stream.of(new Person(1, "hello1", 1), new Person(2, "hello2", 3)); //Method 4 of creating Stream: create infinite Stream (less used) //1) Iteration public static < T > stream < T > iterate (final t seed, final unaryoperator < T > F) //seed - start UnaryOperator - functional interface, the operation you want to perform limit() terminates forEach() iterates over each element Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);//Traverse the first ten even numbers //2) Generate public static < T > stream < T > generate (supplier < T > s) Stream.generate(Math::random).limit(10).forEach(System.out::println); } }
Stream intermediate operation
Multiple intermediate operations can be connected to form a pipeline. Unless the operation is started and terminated on the pipeline, the intermediate operation will not perform any processing! When the operation is terminated, it is processed all at once, which is called "lazy evaluation".
1. Screening and slicing
method | describe |
---|---|
filter(Predicate p) | Accept lambda to exclude some elements from the stream. |
distinct() | Filter to remove duplicate elements through hashCode() and equals() of the elements generated by the stream. |
limit(long maxSize) | Truncate the stream so that its elements do not exceed the given number. |
skip(long n) | Skip elements and return a stream that throws away the first n elements. If there are less than n elements in the stream, an empty stream is returned. Complementary to limit(). |
@Test public void test02(){ List<Person> personList = getPersonList(); Stream<Person> personStream = personList.stream(); //Find an age greater than 12 and output personStream.filter(e->e.getAge()>12).forEach(System.out::println); System.out.println("--------------------------"); //personStream. Cannot be used at this time There are other Stream methods () because the personStream Stream has been destroyed and needs to be recreated //The first two data older than 12 are and output personList.stream().filter(e->e.getAge()>12).limit(2).forEach(System.out::println); System.out.println("--------------------------"); //Skip the first three elements and print personList.stream().skip(3).forEach(System.out::println); System.out.println("--------------------------"); //Filter and remove duplicate data through hashCode() and equals() generated by the stream personList.stream().distinct().forEach(System.out::println); }
2. Mapping
method | describe |
---|---|
map(Function f) | Taking a function as an argument, the function is applied to each element and mapped to a new element. |
mapToDouble(ToDoubleFunction f) | Taking a function as an argument, the function will be applied to each element to generate a new DoubleStream stream. |
mapToInt(ToIntFunction f) | Take a function as an argument, which will be applied to each element to generate a new IntStream stream stream. |
mapToLong(ToLongFunction f) | Take a function as an argument, which will be applied to each element to generate a new LongStream stream. |
flatMap(Function f) | Take a stream as a parameter, then replace each value in the stream with another stream, and finally connect all streams into one stream. |
@Test public void test03(){ List<String> stringList = Arrays.asList("aa","bb","cc","dd"); stringList.stream().map(String::toUpperCase).forEach(System.out::println); System.out.println("================="); List<Person> personList = getPersonList(); personList.stream().filter(e->e.getName().length()>4).map(Person::getId).forEach(System.out::println); }
3. Sorting
method | describe |
---|---|
sorted() | Generate a new stream, which is sorted in natural order |
sorted(Comparator com) | Generates a stream that is sorted by comparison container order |
@Test public void test04(){ List<Integer> integers = Arrays.asList(12, 43, 25, 78, 10, 28); integers.stream().sorted().forEach(System.out::println); System.out.println("-----------------"); getPersonList().stream().sorted((e1,e2)->Integer.compare(e1.getAge(),e2.getAge())).forEach(System.out::println); }
Stream termination operation
1. Find and match
method | describe |
---|---|
allMatch(Predicate p) | Check that all elements match |
anyMathch(Predicate p) | Check to see if at least one element matches |
noneMatch(Predicate p) | Check that no elements match |
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 Collectinon interface requires users to do iteration, which is called external iteration. On the contrary, the Stream API uses internal iteration - that is, it helps you do the iteration) |
@Test public void test05(){ //Is everyone older than 12 boolean b = getPersonList().stream().allMatch(person -> person.getAge() > 12); System.out.println(b); //Is there at least one person older than 12 boolean b1 = getPersonList().stream().anyMatch(person -> person.getAge() > 12); System.out.println(b1); //Check that no one is under the age of 12 boolean b2 = getPersonList().stream().noneMatch(person -> person.getAge() < 12); System.out.println(b2); //Return the first person matched Optional<Person> person1 = getPersonList().stream().filter(person -> person.getAge()>13).findFirst(); System.out.println(person1); //Returns any element that matches Optional<Person> person2 = getPersonList().stream().filter(person -> person.getAge() > 13).findAny(); System.out.println(person2); //Return record quantity long count = getPersonList().stream().count(); System.out.println(count); //Returns the maximum value Optional<Integer> max = getPersonList().stream().map(person -> person.getAge()).max(Integer::compare); System.out.println(max); //Returns the maximum value Optional<Person> min = getPersonList().stream().min((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())); System.out.println(min); }
2. Reduction
method | describe |
---|---|
reduce(T iden,BinaryOperator b) | You can combine the elements in the stream repeatedly to get a value and return T |
reduct(BinartOperator b) | You can combine the elements in the flow repeatedly to get a value. Return to optional < T > |
@Test public void test06(){ List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer reduce = list.stream().reduce(0, Integer::sum); System.out.println(reduce); Integer reduce1 = getPersonList().stream().map(person -> person.getAge()).reduce(1, (e1, e2) -> (e1 * e2)); System.out.println(reduce1); }
3. Collection
method | describe |
---|---|
collect(Collector c) | Convert the Stream to another form. Accept the implementation of a Collector interface, which is used to summarize the elements in the Stream. |
The implementation of the method in the Collector interface determines how to perform collection operations (such as collecting list, set and map).
In addition, the Collectors utility class provides many static methods to easily create collector instances. The specific methods are as follows:
method | Return type | effect | |
---|---|---|---|
toList | List<T> | Collect the elements in the stream into a List | list.stream().collect(Collectors.toList()) |
toSet | Set<T> | Collect the elements in the stream into a Set | list.stream().collect(Collectors.toSet()) |
toCollection | Collection<T> | Collect the elements in the stream into the created collection | list.stream().collect(Collectors.toCollection(ArrayList::new)) |
counting | Long | Calculate the number of elements in the flow | list.stream().collect(Collectors.counting) |
summingInt | Integer | Calculates the integer attributes and values of the elements in the stream | list.stream().collect(Collectors.summingInt(Person::getAge)) |
averageingInt | Double | Calculates the average value of the Integer attribute of the element in the flow | list.stream().collect(Collectors.averageingInt(Person::getAge)) |
summarizingInt | IntSummaryStatistics | Collect statistics for the Integer attribute in the stream. E.g. average value | list.stream().collect(Collectors.summarizingInt(Person::getAge)) |
joining | String | Each string in the connection stream | list.stream().map(Person::getName).collect(Collectors.joining()) |
maxBy | Optional<T> | Select the maximum value according to the comparator | list.stream().collect(Collectors.maxBy(comparingInt(Person::getAgge))) |
minBy | Optional<T> | Select the minimum value according to the comparator | list.stream().collect(Collectors.minBy(comparingInt(Person::getAgge))) |
reducing | Type of reduction | Starting from an initial value as an accumulator, BinaryOperator is combined with the elements in the stream one by one to reduce it to a single value. | list.stream().collect(Collectors.reducing(0,Person::getAge,Integer::sum)) |
collectingAndThen | The type returned by the conversion function | Wrap another collector and convert its result to a function | list.stream().collect(Collectors.collectingAndThen(Collectors.toList(),List::size)) |
groupingBy | Map<K,List<T>> | According to an attribute value, the attribute is K and the result is V | list.stream().collect(Collectors.groupingBy(Person::getAge)) |
paritioningBy | Map<Boolean,List<T>> | Partition according to True or False | list.stream().collect(Collectors.paritioningBy(Person::getManage)) |