Java8的其它新特性
Java8新特性简介
- 速度更快
- 代码更少(增加了新的语法:
Lambda表达式) - 强大的
Stream API - 便于并行
- 最大化减少空指针异常
- Nashorn引擎,允许在 JVM上运行 JS应用
并行流与串行流
- 并行流:就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流;相比较串行流,
并行的流可以很大程度上提高程序的执行效率 - Java 8中将并行进行了优化,我们可以很容易的对数据进行操作
- Stream API 可以声明性地通过
parallel()、sequential()在并行流与串行流之间进行切换
Lambda表达式 *
- 一套新的语法规则
Lambda表达式的本质:作为函数式接口的实例,也就是说没有接口就没有意义- 此接口只能有一个抽象方法(
函数式接口),因此,没有必要写出方法的名称 - 为接口提供具体的实现类的对象时,可以简写为 Lambda表达式
- 此接口只能有一个抽象方法(
为什么使用 Lambda表达式
Lambda是一个 匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递);使用它可以写出更简洁、更灵活的代码;作为一种更紧凑的代码风格,使 Java的语言表达能力得到了提升
Lambda表达式的使用
-
举例:(o1, o2) -> Integer.compare(o1, o2);
-
格式:
- ->:Lambda操作符 或 箭头操作符
- ->左边:Lambda形参列表(接口中抽象方法的形参列表)
- ->右边:Lambda体(就是重写的抽象方法的方法体)
-
使用条件
- 接口必须是函数式接口
- 用匿名实现类表示的对象,现在都可以使用 Lambda表达式
实例(六种格式)
package com.atguigu.java1;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.function.Consumer;
/**
* @author lv
* @create 2021-03-08 19:28
*
* Lambda表达式的使用:
*
* 1.举例:(o1, o2) -> Integer.compare(o1, o2);
* 2.格式:
* ->:Lambda操作符 或 箭头操作符
* ->左边:Lambda形参列表(接口中抽象方法的形参列表)
* ->右边:Lambda体(就是重写的抽象方法的方法体)
*
* 3.Lambda表达式的使用:(分6种情况)
* 总结:
* ->左边:Lambda形参列表的参数类型可以省略(类型推断),当参数列表只有一个参数时可以省略括号
* ->右边:Lambda体应该使用一对 {}包裹;
* 如果 Lambda体只有一条执行语句(包括 return语句)可以省略花括号和 return关键字
*/
public class LambdaTest1 {
/**
* 语法格式一:无参、无返回值
*/
@Test
public void test () {
// 创建一个接口实现类的对象
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("了哎北京");
}
};
r1.run(); // 了哎北京
System.out.println("使用 Lambda表达式重写以上代码");
// ->:Lambda操作符
Runnable r2 = () -> System.out.println("了哎北京啊");
r2.run(); // 了哎北京啊
}
/**
* 语法格式二:Lambda需要一个参数,但没有返回值
*/
@Test
public void test2 () {
Consumer<String> cons = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
cons.accept("123"); // 123
Consumer<String> cons1 = (String str) -> {
System.out.println(str);
};
cons1.accept("么同"); // 么同
}
/**
* 语法格式三:数据类型可以省略,因为可由编译期推断得出,称为“类型推断”
*
*/
@Test
public void test3 () {
Consumer<String> cons = new Consumer<String>() {
@Override
public void accept(String str) {
System.out.println(str);
}
};
cons.accept("123"); // 123
Consumer<String> cons1 = (str) -> {
System.out.println(str);
};
cons1.accept("么同"); // 么同
// 其它类型推断
ArrayList<String> list = new ArrayList<>(); // 类型推断 - 泛型
int[] arr = new int[]{1, 2, 3};
int[] arr1 = {1, 2, 3}; // 类型推断 - 类型
}
/**
* 语法格式四:Lambda若只需要一个参数时,参数的小括号也可以省略
*/
@Test
public void test4 () {
Consumer<String> cons1 = (str) -> {
System.out.println(str);
};
cons1.accept("么同"); // 么同
Consumer<String> cons2 = (str) -> {
System.out.println(str);
};
cons2.accept("么同"); // 么同
}
/**
* 语法格式五:Lambda若需要两个以上参数时,多条执行语句,并且可以有返回值
*/
@Test
public void test5 () {
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
int compare1 = com1.compare(10, 20);
System.out.println(compare1); // -1
Comparator<Integer> com2 = (o1, o2) -> {
System.out.println("输出了"); // 输出了
return o1.compareTo(o2);
};
int compare2 = com2.compare(111, 21);
System.out.println(compare2); // 1
}
/**
* 语法格式六:当 Lambda体只有一条语句时,return与 花括号都可以考虑省略
*/
@Test
public void test6 () {
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
int compare1 = com1.compare(10, 20);
System.out.println(compare1); // -1
Comparator<Integer> com2 = (o1, o2) -> o1.compareTo(o2);
int compare2 = com2.compare(88, 104);
System.out.println(compare2); // -1
}
}
函数式接口(FunctionalInterface)
如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口
何为函数式接口
- 只有一个抽象方法的接口,称为函数式接口
- 可以通过 Lambda表达式来创建该接口的匿名实现类的对象
- 可以通过在一个接口上使用 @FunctionalInterface注解,这样做可以验证它是否是一个函数式接口
- 在 java.util.function包下定义了 Java8丰富的函数式接口
- 相当于
函数式接口的实例就是 Lambda表达式
何时使用函数式接口
- 开发中需要定义函数式接口时,首先查找 jdk提供的函数式接口是否满足,在决定是否重新定义
Java 内置四大核心函数式接口
Customer消费型接口
- 参数类型:T
- 返回类型:void
- 用途:对类型为 T的对象应用操作,包含方法:void accept(T t)
Supplier供给型接口
- 参数类型:无
- 返回类型:T
- 用途:返回类型为 T的对象,包含方法:T get()
Function<T, R>函数型接口
- 参数类型:T
- 返回类型:R
- 用途:对类型为 T的对象应用操作,并返回结果;结果是 R类型的对象;包含方法:R apply(T t)
Predicate断定型接口
- 参数类型:T
- 返回类型:boolean
- 用途:确定类型为 T的对象是否满足约束,并返回 boolean值;包含方法:boolean test(T t)
实例
package com.atguigu.java1;
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;
/**
* @author lv
* @create 2021-03-09 20:04
*
* java内置四大核心函数式接口
*
* 1.消费型接口:Consumer<T> void accept()
* 2.供给型接口:Supplier<T> T get()
* 3.函数型接口:Function<T, R> R apply(T t)
* 4.断定型接口:Predicate<T> boolean test(T t)
*
*/
public class LambdaTest2 {
@Test
public void test () {
Consumer<String> con1 = (str) -> System.out.println(str);
con1.accept("谎言和誓言的区别:");
Supplier<String> sup1 = () -> "一个是听的当真了,一个是说的当真了";
String str = sup1.get();
System.out.println(str);
Function<String, Integer> f1 = (o1) -> Integer.parseInt(o1);
int apply = (int) f1.apply("123");
System.out.println(apply);
Predicate<Integer> p1 = (n) -> n > 12;
boolean test = p1.test(1);
System.out.println(test);
}
@Test
public void test1 () {
happyTime(2000, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("人间消费:" + aDouble);
// 人间消费:2000.0
}
});
// 使用 Lambda表达式
happyTime(1000, (money) -> System.out.println("人间消费:" + money));
// 人间消费:1000.0
}
public void happyTime (double money, Consumer<Double> con) {
con.accept(money);
}
@Test
public void test2 () {
List<String> list = Arrays.asList("北京", "西京", "南京", "东京", "天津");
List<String> strings = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(strings); // [北京, 西京, 南京, 东京]
// 使用 Lambda表达式
List<String> filterString = filterString(list, s -> s.contains("京"));
System.out.println(filterString); // [北京, 西京, 南京, 东京]
}
public List<String> filterString (List<String> list, Predicate<String> pre) {
ArrayList<String> fList = new ArrayList<>();
for (String s : list) {
// Lambda调用
if (pre.test(s)) {
fList.add(s);
}
}
return fList;
}
}
方法引用与构造器引用
方法引用(Method References)
-
当要传递给 Lambda体的操作,已经有实现的方法了,可以使用方法引用
-
方法引用可以看作是 Lambda表达式的深层表达;换句话说,方法引用就是 Lambda表达式,也就是函数式接口的匿名实现类的对象,通过方法的名字来指向一个方法,可以认为是 Lambda表达式的一个语法糖
-
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致
-
格式:使用操作符 “ :: ” 将类(或对象)与方法名分隔开来
-
如下三种主要使用情况:
- 情况一,对象 :: 实例方法名
- 情况二,类 :: 静态方法名
- 情况三,类 :: 实例方法名
方法引用使用的要求
-
一. 要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同!(针对于情况1和情况2)
-
二. 当函数式接口方法的第一个参数是需要引用方法的调用者,并且第二个参数是需要引用方法的参数(或无参数)时:ClassName :: methodName(针对情况3)
实例
package com.atguigu.java2;
import org.junit.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 方法引用的使用
*
* 1.使用情境:当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!
*
* 2.方法引用,本质上就是Lambda表达式,而Lambda表达式作为函数式接口的实例。所以
* 方法引用,也是函数式接口的实例。
*
* 3. 使用格式: 类(或对象) :: 方法名
*
* 4. 具体分为如下的三种情况:
* 情况1 对象 :: 非静态方法
* 情况2 类 :: 静态方法
*
* 情况3 类 :: 非静态方法 // 此方法不太能想到,有难度
*
* 5. 方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的
* 形参列表和返回值类型相同!(针对于情况1和情况2)
*
* Created by shkstart.
*/
public class MethodRefTest {
// 情况一:对象 :: 实例方法
//Consumer中的void accept(T t)
//PrintStream中的void println(T t)
@Test
public void test1() {
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("北京");
System.out.println("*******************");
PrintStream ps = System.out;
Consumer<String> con2 = ps::println;
con2.accept("beijing");
}
@Test
public void test11 () {
Consumer<String> cons = str -> System.out.println(str);
cons.accept("倒数 1234567");
PrintStream ps = System.out;
Consumer<String> cons1 = ps :: print;
cons1.accept("Come on boy");
}
//Supplier中的T get()
//Employee中的String getName()
@Test
public void test2() {
Employee emp = new Employee(1001,"Tom",23,5600);
Supplier<String> sup1 = () -> emp.getName();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<String> sup2 = emp::getName;
System.out.println(sup2.get());
}
@Test
public void test22 () {
Employee emp = new Employee(1002, "Tommer", 15, 8520);
Supplier<String> sp = () -> emp.getName();
String string = sp.get();
System.out.println(string);
Supplier<String> sp1 = emp :: getName;
String s = sp1.get();
System.out.println(s);
}
// 情况二:类 :: 静态方法
//Comparator中的int compare(T t1,T t2)
//Integer中的int compare(T t1,T t2)
@Test
public void test3() {
Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
System.out.println(com1.compare(12,21));
System.out.println("*******************");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(12,3));
}
@Test
public void test33 () {
Comparator<Integer> com = (o1, o2) -> Integer.compare(o1, o2);
System.out.println(com.compare(123, 456)); // -1
Comparator<Integer> com1 = Integer :: compare;
System.out.println(com1.compare(111, 123)); // -1
/**
* 移花接木:相同的返回值类型、形参列表类型
*/
}
//Function中的R apply(T t)
//Math中的Long round(Double d)
@Test
public void test4() {
Function<Double,Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
System.out.println("*******************");
Function<Double,Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(12.3));
System.out.println("*******************");
Function<Double,Long> func2 = Math::round;
System.out.println(func2.apply(12.6));
}
@Test
public void test44 () {
Function<Double, Long> f1 = d -> Math.round(d);
Long aLong = f1.apply(2.4);
System.out.println(aLong); // 2
Function<Double, Long> f11 = Math ::round;
Long aLong1 = f11.apply(12.5);
System.out.println(aLong1); // 13
}
// 情况三:类 :: 实例方法 (有难度)
// Comparator中的int comapre(T t1,T t2)
// String中的int t1.compareTo(t2)
@Test
public void test5() {
Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc","abd"));
System.out.println("*******************");
Comparator<String> com2 = String :: compareTo;
System.out.println(com2.compare("abd","abm"));
}
@Test
public void test55 () {
Comparator<String> com = (s1, s2) -> s1.compareTo(s2);
int compare = com.compare("abc", "abd");
System.out.println(compare); // -1
Comparator<String> com1 = String ::compareTo;
int compare1 = com1.compare("ac", "ad");
System.out.println(compare1); // -1
}
//BiPredicate中的boolean test(T t1, T t2);
//String中的boolean t1.equals(t2)
@Test
public void test6() {
BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
System.out.println(pre1.test("abc","abc"));
System.out.println("*******************");
BiPredicate<String,String> pre2 = String :: equals;
System.out.println(pre2.test("abc","abd"));
}
@Test
public void test66 () {
// BiPredicate<String, String> bp = (s1, s2) -> s1.equals(s2);
BiPredicate<String, String> bp = (s1, s2) -> s1.contains(s2);
boolean test = bp.test("123", "2");
System.out.println(test); // true
BiPredicate<String, String> bp1 = String :: equals;
boolean test1 = bp1.test("ab", "ad");
System.out.println(test1); // false
}
// Function中的R apply(T t)
// Employee中的String getName();
@Test
public void test7() {
Employee employee = new Employee(1001, "Jerry", 23, 6000);
Function<Employee,String> func1 = e -> e.getName();
System.out.println(func1.apply(employee));
System.out.println("*******************");
Function<Employee,String> func2 = Employee::getName;
System.out.println(func2.apply(employee));
}
@Test
public void test77 () {
Employee e1 = new Employee(1022, "Jerry", 22, 963);
Function<String, Integer> f1 = s -> Integer.parseInt(s);
Integer apply = f1.apply("123");
System.out.println(apply); // 123Function<String, Integer> f1 = s -> Integer.parseInt(s);
Function<Employee, String> f11 = s -> s.getName();
String apply11 = f11.apply(e1);
System.out.println(apply11); // Jerry
Function<Employee, String> f2 = Employee :: getName;
String apply1 = f2.apply(e1);
System.out.println(apply1); // Jerry
}
}
构造器引用
- 格式:类名 :: new
- 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致
实例
package com.atguigu.java2;
import org.junit.Test;
import java.util.Arrays;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 一、构造器引用
* 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
* 抽象方法的返回值类型即为构造器所属的类的类型
*
* 二、数组引用
* 我们可以把数组看做是一个特殊的类,则写法与构造器引用一致。
*
* Created by shkstart
*/
public class ConstructorRefTest {
//构造器引用
//Supplier中的T get()
//Employee的空参构造器:Employee()
@Test
public void test1(){
Supplier<Employee> sup = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println("*******************");
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
System.out.println("*******************");
Supplier<Employee> sup2 = Employee :: new;
System.out.println(sup2.get());
}
@Test
public void test11 () {
Supplier<Employee> sup = () -> new Employee();
Employee emp1 = sup.get();
System.out.println(emp1);
// 构造器引用
Supplier<Employee> sup2 = Employee :: new;
Employee emp2 = sup2.get();
System.out.println(emp2);
}
//Function中的R apply(T t)
@Test
public void test2(){
Function<Integer,Employee> func1 = id -> new Employee(id);
Employee employee = func1.apply(1001);
System.out.println(employee);
System.out.println("*******************");
Function<Integer,Employee> func2 = Employee :: new;
Employee employee1 = func2.apply(1002);
System.out.println(employee1);
}
@Test
public void test22 () {
Function<Integer, Employee> f = id -> new Employee(id);
Employee emp1 = f.apply(123);
System.out.println(emp1);
Function<Integer, Employee> f1 = Employee ::new;
Employee emp2 = f1.apply(456);
System.out.println(emp2);
}
//BiFunction中的R apply(T t,U u)
@Test
public void test3(){
BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
System.out.println(func1.apply(1001,"Tom"));
System.out.println("*******************");
BiFunction<Integer,String,Employee> func2 = Employee :: new;
System.out.println(func2.apply(1002,"Tom"));
}
@Test
public void test33 () {
BiFunction<Integer, String, Employee> f1 = (id, name) -> new Employee(id, name);
Employee tom = f1.apply(789, "Tom");
System.out.println(tom);
BiFunction<Integer, String, Employee> f2 = Employee :: new;
Employee jerry = f2.apply(741, "Jerry");
System.out.println(jerry);
}
//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("*******************");
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}
@Test
public void test44 () {
Function<Integer, Integer[]> f1 = i -> new Integer[i];
Integer[] arrInt = f1.apply(3);
System.out.println(arrInt.length); // 3
System.out.println(Arrays.toString(arrInt)); // [null, null, null]
Function<Integer, String[]> f2 = String[] :: new;
String[] arrStr = f2.apply(5);
System.out.println(arrStr.length); // 5
System.out.println(Arrays.toString(arrStr)); // [null, null, null, null, null]
}
}
数组引用
- 格式:数组类型[] :: new
- 我们可以把数组看做是一个特殊的类,则写法与构造器引用一致
实例
//数组引用
//Function中的R apply(T t)
@Test
public void test4(){
Function<Integer,String[]> func1 = length -> new String[length];
String[] arr1 = func1.apply(5);
System.out.println(Arrays.toString(arr1));
System.out.println("*******************");
Function<Integer,String[]> func2 = String[] :: new;
String[] arr2 = func2.apply(10);
System.out.println(Arrays.toString(arr2));
}
@Test
public void test44 () {
Function<Integer, Integer[]> f1 = i -> new Integer[i];
Integer[] arrInt = f1.apply(3);
System.out.println(arrInt.length); // 3
System.out.println(Arrays.toString(arrInt)); // [null, null, null]
Function<Integer, String[]> f2 = String[] :: new;
String[] arrStr = f2.apply(5);
System.out.println(arrStr.length); // 5
System.out.println(Arrays.toString(arrStr)); // [null, null, null, null, null]
}
强大的 Stream API *
使用 Stream API的原因
Collection是一种静态的内存数据结构,而 Stream是有关计算的;前者主要面向内存,数据存储在内存中,后者主要面向 CPU,通过 CPU实现计算
Stream是什么
是数据渠道,用于操作数据源(集合、数组等)所产生的元素序列;集合讲的是数据,Stream讲的是计算
注意:
- Stream自己不会存储元素(类似于迭代器)
- Stream不会改变源对象;相反,它们会返回一个持有结果的新 Stream
Stream操作是延迟执行的;这意味着它们会等到需要结果时才执行
Stream操作的三个步骤
- 创建 Stream
一个数据源(如:集合、数组),获取一个流
- 中间操作
一个中间操作链,对数据源的数据进行处理
- 终止操作(终端操作)
一旦执行终止操作,就执行中间操作链,并产生结果;之后,不会再被使用
创建 Stream的方式(实例化)
方式一:通过集合
Java8中的 Collection接口被扩展,提供了两个获取流的方法:
- default Stream stream():返回一个顺序流
- default Stream parallelStream():返回一个并行流
// 创建 Stream的方式一:通过集合
@Test
public void test() {
List<Employee> employees = EmployeeData.getEmployees();
// default Stream<E> stream():返回一个顺序流
Stream<Employee> stream = employees.stream();
// default Stream<E> parallelStream():返回一个并行流
Stream<Employee> parallelStream = employees.parallelStream();
}
方式二:通过数组
Java8中的 Arrays的静态方法 stream()可以获取数组流:
- static Stream stream(T[] array):返回一个流
重载形式,能够处理对应基本类型的数组:
- public static IntStream stream(int[] array)
- public static LongStream stream(long[] array)
- public static DoubleStream stream(double[] array)
/**
* 方式二:通过数组
*/
@Test
public void test1 () {
int[] ints = {1, 2, 3, 4, 5, 6};
// 调用 Arrays类的 static <T> Stream<T> stream(T[] array):返回一个流
IntStream stream = Arrays.stream(ints);
Employee e1 = new Employee(1001, "Tom");
Employee e2 = new Employee(1002, "Jerry");
Employee[] arrEmps = new Employee[]{e1, e2};
Stream<Employee> employeeStream = Arrays.stream(arrEmps);
}
方式三:通过 Stream的 of()
可以调用 Stream类静态方法 of(),通过显示值创建一个流;它可以接收任意数量的参数
- public static Stream of(T... values):返回一个流
/**
* 方式三:通过 Stream的 of()
*/
@Test
public void test2 () {
Stream<Integer> integerStream = Stream.of(1, 2, 3);
}
方式四:创建无限流
可以使用静态方法 Stream.iterate()和 Stream.generate(),创建无限流
- 迭代:public static Stream iterate(final T seed, final UnaryOperator f)
- 生成:public static Stream generate(Supplier s)
/**
* 方式四:创建无限流
*/
@Test
public void test3 () {
// - 迭代:public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out :: println);
// - 生成:public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math ::random).limit(10).forEach(System.out :: println);
}
Stream 的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为惰性求值
1.筛选与切片
- filter(Predicate p):接收 Lambda,从流中排除某些元素
- distinct():筛选,通过流所生成元素的 hashCode()和 equals()去除重复元素
- limit(long maxSize):截断流,使其元素不超过给定数量
- skip(long n):跳过元素,返回一个扔掉了前 n个元素的流;若流中元素不足 n个,则返回一个空流;与 limit(n)互补
package com.atguigu.java3;
import com.atguigu.java2.Employee;
import com.atguigu.java2.EmployeeData;
import org.junit.Test;
import java.util.List;
import java.util.stream.Stream;
/**
* @author lv
* @create 2021-03-14 10:39
*/
public class StreamAPITest1 {
@Test
public void test () {
List<Employee> employeeList = EmployeeData.getEmployees();
Stream<Employee> stream = employeeList.stream();
// 1.filter(Predicate p):接收 Lambda,从流中排除某些元素
// stream.filter(e -> e.getSalary() > 7000);
// 查询员工表中薪资大于 7000的员工信息
stream.filter(e -> e.getSalary() > 7000).forEach(System.out :: println);
// 2.limit(long maxSize):截断流,使其元素不超过给定数量
// stream.limit(5).forEach(System.out :: println);
// error:IllegalStateException: stream has already been operated upon or closed
System.out.println();
employeeList.stream().limit(5).forEach(System.out :: println);
/**
* 3.skip(long n):跳过元素,返回一个扔掉了前 n个元素的流;
* 若流中元素不足 n个,则返回一个空流;与 limit(n)互补
*/
employeeList.stream().skip(5).forEach(e -> System.out.println(e.getName()));
/**
* 比尔盖茨
* 任正非
* 扎克伯格
*/
/**
* 4.distinct():筛选,通过流所生成元素的 hashCode()和 equals()去除重复元素
*/
employeeList.add(new Employee(1010, "刘东", 40, 8080));
employeeList.add(new Employee(1010, "刘东", 40, 8080));
employeeList.add(new Employee(1010, "刘东", 40, 8080));
employeeList.add(new Employee(1010, "刘东", 40, 8080));
employeeList.stream().distinct().forEach(System.out :: println);
}
}
2.映射
- map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream
- mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream
- mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream
- flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
@Test
public void test1 () {
List<String> strList = Arrays.asList("aa", "bb", "dd", "ee");
/**
* map(Function f):接收一个函数作为参数,
* 该函数会被应用到每个元素上,并将其映射成一个新的元素
*/
strList.stream().map(str -> str.toUpperCase()).forEach(System.out :: println);
/**
* AA
* BB
* DD
* EE
*/
// 练习1:获取员工姓名长度大于3的员工的姓名
List<Employee> employeeList = EmployeeData.getEmployees();
// employeeList.stream().map(e -> e.getName()).filter(e -> e.length() > 3).forEach(System.out :: println);
// employeeList.stream().map(Employee :: getName ).filter(e -> e.length() > 3).forEach(System.out :: println);
Stream<String> namesStream = employeeList.stream().map(Employee::getName);
namesStream.filter(n -> n.length() > 3).forEach(System.out :: println);
/**
* 比尔盖茨
* 扎克伯格
*/
System.out.println();
// 练习2:
Stream<Stream<Character>> streamStream = strList.stream().map(StreamAPITest1 :: fromStringToStream);
streamStream.forEach(s -> {
// System.out.println("*");
s.forEach(System.out :: println);
});
/**
* a
* a
* b
* b
* d
* d
* e
* e
*/
/**
* flatMap(Function f):接收一个函数作为参数,
* 将流中的每个值都换成另一个流,然后把所有流连接成一个流
*/
System.out.println();
Stream<Character> characterStream = strList.stream().flatMap(StreamAPITest1::fromStringToStream);
characterStream.forEach(System.out :: println);
/**
* a
* a
* b
* b
* d
* d
* e
* e
*/
}
// 将字符串中的多个字符构成的集合转换为对应的 Stream的实例
public static Stream<Character> fromStringToStream (String str) {
ArrayList<Character> charList = new ArrayList<>();
for(Character c : str.toCharArray()) {
charList.add(c);
}
return charList.stream();
}
@Test
public void test2 () {
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
ArrayList list2 = new ArrayList();
list2.add(4);
list2.add(5);
list2.add(6);
// list1.add(list2);
// System.out.println(list1); // [1, 2, 3, [4, 5, 6]]
list1.addAll(list2);
System.out.println(list1); // [1, 2, 3, 4, 5, 6]
}
3.排序
- sorted():产生一个新流,其中按自然顺序排序
- sorted(Compatator com):产生一个新流,其中按比较器顺序排序
/**
* 3.排序
*/
@Test
public void test3 () {
/**
* sorted():自然排序
*/
List<Integer> integerList = Arrays.asList(12, 456, 78, 5, 63, 5, 45);
integerList.stream().sorted().forEach(System.out :: println);
List<Employee> employeesList = EmployeeData.getEmployees();
// employeesList.stream().sorted().forEach(System.out :: println);
/**
* error:com.atguigu.java2.Employee cannot be cast to java.lang.Comparable
* Employee类没有实现 Comparable接口
*/
/**
* sorted(Comparator com):定制排序
*/
// employeesList.stream()
// .sorted((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()))
// .forEach(System.out :: println);
employeesList.stream()
.sorted((e1, e2) -> {
int compare = Integer.compare(e1.getAge(), e2.getAge());
if (compare != 0) {
return compare;
} else {
return -Double.compare(e1.getSalary(), e2.getSalary());
}
}).forEach(System.out :: println);
}
Stream的终止操作
- 终端操作会从流的流水线生成结果;其结果可以是任何不是流的值,例如:List、Integer,甚至是 void
- 流只要进行了终止操作后,就不能再次使用
1.匹配与查找
- allMatch(Predicate p):检查是否匹配所有元素
- anyMatch(Predicate p):检查是否至少匹配一个元素
- noneMatch(Predicate p):检查是否没有匹配所有元素
- findFirst():返回第一个元素
- findAny():返回当前流中的任意元素
- count():返回流中元素总数
- max(Comparator c):返回流中最大值
- min(Comparator c):返回流中最小值
- forEach(Consumer c):
内部迭代(使用 Collection接口需要用户去做迭代,称为外部迭代;相反,Stream API使用内部迭代 - 它帮你把迭代做了)
package com.atguigu.java3;
import com.atguigu.java2.Employee;
import com.atguigu.java2.EmployeeData;
import org.junit.Test;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
/**
* @author lv
* @create 2021-03-14 17:26
*/
public class StreamAPITest2 {
/**
* 1.匹配与查找
*/
@Test
public void test () {
List<Employee> employeeList = EmployeeData.getEmployees();
/**
* allMatch(Predicate p):检查是否匹配所有元素
* 练习:是否所有员工的年龄都大于 18岁
*
*/
boolean allMatch = employeeList.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch); // false
/**
* anyMatch(Predicate p):检查是否至少匹配一个元素
*/
boolean anyMatch = employeeList.stream().anyMatch(e -> e.getSalary() > 10000);
System.out.println(anyMatch); // false
/**
* noneMatch(Predicate p):检查是否没有匹配所有元素
* 练习:是否存在员工姓名:姓 “雷”
*/
boolean noneMatch = employeeList.stream().noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(noneMatch); // false
/**
* findFirst():返回第一个元素
*
*/
Optional<Employee> first = employeeList.stream().findFirst();
System.out.println(first); // Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
/**
* findAny():返回当前流中的任意元素
*/
// Optional<Employee> any = employeeList.stream().findAny();
Optional<Employee> any = employeeList.parallelStream().findAny();
System.out.println(any); // Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
/**
* count():返回流中元素总数
*/
long count = employeeList.stream().count();
System.out.println(count); // 8
/**
* max(Comparator c):返回流中最大值
* 返回最高工资
*/
Optional<Employee> maxEmployee = employeeList.stream().max((e1, e2) -> {
return Double.compare(e1.getSalary(), e2.getSalary());
});
Stream<Double> doubleStream = employeeList.stream().map(e -> e.getSalary());
Optional<Double> max = doubleStream.max(Double :: compare);
System.out.println(max); // Optional[9876.12]
System.out.println(maxEmployee); // Optional[Employee{id=1002, name='马云', age=12, salary=9876.12}]
/**
* min(Comparator c):返回流中最小值
*/
// Optional<Employee> minEmployee = employeeList.stream().min((e1, e2) -> {
// return Double.compare(e1.getSalary(), e2.getSalary());
// });
Optional<Employee> minEmployee = employeeList.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(minEmployee); // Optional[Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}]
/**
* forEach(Consumer c):`内部迭代`(使用 Collection接口需要用户去做迭代,称为`外部迭代`;
* 相反,Stream API使用内部迭代 - 它帮你把迭代做了)
*/
employeeList.stream().forEach(System.out :: println);
// default void forEach(Consumer con):集合中的方法
employeeList.forEach(System.out :: println);
}
}
2.归约
-
reduce(T iden, BinaryOperator b):可以将流中元素反复结合起来,得到一个值;返回T
-
reduce(BinaryOperator b):可以将流中元素反复结合起来,得到一个值;返回 Optional
-
备注:map和 reduce的连接通常称为 map-reduce模式,因 Google用它来进行网络搜索而出名
/**
* 2.归约
*/
@Test
public void test1 () {
/**
* reduce(T iden, BinaryOperator b):
* 可以将流中元素反复结合起来,得到一个值;返回T
*/
List<Integer> asList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer reduceSum = asList.stream().reduce(0, Integer::sum);
System.out.println(reduceSum);
/**
* reduce(BinaryOperator b):
* 可以将流中元素反复结合起来,得到一个值;返回 Optional<T>
*/
List<Employee> employeeList = EmployeeData.getEmployees();
Stream<Double> doubleStream = employeeList.stream().map(Employee::getSalary);
// Optional<Double> salarySum = doubleStream.reduce(Double::sum); 或
Optional<Double> salarySum = doubleStream.reduce((d1, d2) -> d1 + d2);
System.out.println(salarySum);
}
3.收集
-
collect(Collector c):将流转换为其它形式;接收一个 Collector接口的实习,用于给 Stream中元素做汇总的方法
-
说明:Collector接口中方法的实现决定了如何对流执行收集的操作(如:收集到 List、Set、Map);另外,Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
- toList:将流中元素收集到 List
- toSet:将流中元素收集到 Set
- toCollection:将流中元素收集到创建的集合中
- counting:计算流中元素的个数
- summingInt:对流中元素的整数属性求和
- averagingInt:计算流中元素 Integer属性的平均值
- summarizingInt:收集流中 Integer属性的统计值;如:平均值
/**
* 3.收集
*/
@Test
public void test2 () {
/**
* collect(Collector c):将流转换为其它形式;
* 接收一个 Collector接口的实习,用于给 Stream中元素做汇总的方法
* 练习:查找工资大于 6000的员工,结果返回为一个 List或 Set
*/
List<Employee> employeeList = EmployeeData.getEmployees();
List<Employee> collectEmployee = employeeList.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
// System.out.println(collectEmployee);
collectEmployee.forEach(System.out :: println);
/**
* Employee{id=1001, name='马化腾', age=34, salary=6000.38}
* Employee{id=1002, name='马云', age=12, salary=9876.12}
* Employee{id=1004, name='雷军', age=26, salary=7657.37}
* Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
*/
System.out.println();
Set<Employee> collectSet = employeeList.stream().filter(e -> e.getSalary() > 8000).collect(Collectors.toSet());
// System.out.println(collectSet);
collectSet.forEach(System.out :: println);
// for (Employee e : collectSet) {
// System.out.println(e);
// }
/**
*
* * Employee{id=1002, name='马云', age=12, salary=9876.12}
* * Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
*/
}
Optional类
Optional类(java.util.Optional)是一个容器类,它可以保存类型 T的值,代表这个值存在;或者仅仅保存 null,表示这个值不存在;原来用 null表示一个值不存在,现在 Optional可以更好的表达这个概念;并且可以避免空指针异常
Optional类的 Javadoc描述如下:这是一个可以为 null的容器对象;如果值存在则 isPresent()方法会返回 true,调用 get()方法会返回该对象
- Optional提供很多有用的方法,这样我们就不用显式进行空值检测
创建 Optional类对象的方法:
- Optional.of(T t):创建一个 Optional实例,t必须为非空
- Optional.empty():创建一个空的 Optional实例
- optional.ofNullable(T t):t可以为 null
package com.atguigu.java4;
import org.junit.Test;
import java.util.Optional;
/**
* @author lv
* @create 2021-03-16 20:53
*/
public class OptionalTest {
/**
* > 创建 Optional类对象的方法:
*
* - Optional.of(T t):创建一个 Optional实例,t必须为非空
* - Optional.empty():创建一个空的 Optional实例
* - optional.ofNullable(T t):t可以为 null
*/
@Test
public void test () {
Girl girl = new Girl();
// of(T t):必须保证 t必须为非空
Optional<Girl> girl1 = Optional.of(girl);
System.out.println(girl1); // Optional[Girl{name='null'}]
Optional<Object> empty = Optional.empty();
System.out.println(empty); // Optional.empty
girl = null;
// ofNullable(T t):t可以为 null
Optional<Girl> girl2 = Optional.ofNullable(girl);
System.out.println(girl2); // Optional.empty
}
}
判断 Optional容器中是否包含对象:
- boolean isPresent():判断是否包含对象
- void ifPresent(Consumer<? super consumer>):如果有值,就执行 Consumer接口的实现代码,并且该值会作为参数传给它
获取 Optional容器的对象
- T get():如果调用对象包含值,返回该值,否则抛异常
- T orElse(T other):如果有值则将其返回,否则返回指定的 other对象
- T orElseGet(Supplier<? extends T> other):如果有值则将其返回,否则返回由 Supplier接口实现提供的对象
- T orElseThrow(Supplier<? extends X> exceptionSupplier):如果有值则将其返回,否则抛出由 Supplier接口实现提供的异常
package com.atguigu.java4;
import org.junit.Test;
import java.util.Optional;
/**
* @author lv
* @create 2021-03-16 20:53
*
* Optional类:为了在程序中避免出现空指针异常而创建的
*
* 常用方法:ofNullable(T t)、orElse(T t)
*
*/
public class OptionalTest {
/**
* > 创建 Optional类对象的方法:
*
* - Optional.of(T t):创建一个 Optional实例,t必须为非空
* - Optional.empty():创建一个空的 Optional实例
* - optional.ofNullable(T t):t可以为 null
*/
@Test
public void test () {
Girl girl = new Girl();
// of(T t):必须保证 t必须为非空
Optional<Girl> girl1 = Optional.of(girl);
System.out.println(girl1); // Optional[Girl{name='null'}]
Optional<Object> empty = Optional.empty();
System.out.println(empty); // Optional.empty
girl = null;
// ofNullable(T t):t可以为 null
Optional<Girl> girl2 = Optional.ofNullable(girl);
/**
* T orElse(T other):如果有值则将其返回,否则返回指定的 other对象
*/
Girl girl3 = girl2.orElse(new Girl("赵晓笑"));
System.out.println(girl2); // Optional.empty
System.out.println(girl3); // Girl{name='赵晓笑'}
}
/**
* 实例:Optional避免空指针异常
*/
public String getGirlName (Boy boy) {
return boy.getGirl().getName();
}
// 优化后的getGirlName()
public String getGirlName1 (Boy boy) {
if (null != boy) {
Girl girl = boy.getGirl();
if (null != girl)
return girl.getName();
}
return null;
}
// 使用 Optional类的 getGirlName2()
public String getGirlName2 (Boy boy) {
Optional<Boy> optionalBoy = Optional.ofNullable(boy);
// 此时 boy1一定非空
Boy boy1 = optionalBoy.orElse(new Boy(new Girl("晓笑")));
Girl girl = boy1.getGirl();
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
// 此时 girl1一定非空
Girl girl1 = optionalGirl.orElse(new Girl("晓笑小"));
return girl1.getName();
}
/**
* 实例:Optional避免空指针异常
*/
@Test
public void test1 () {
Boy boy = new Boy();
// String girlName = getGirlName(boy);
// System.out.println(girlName); // NullPointerException
String girlName1 = getGirlName1(boy);
System.out.println(girlName1); // null
// 1.
String girlName2 = getGirlName2(boy);
System.out.println(girlName2); // 晓笑小
// 2.
Boy boy1 = null;
String girlName21 = getGirlName2(boy1);
System.out.println(girlName21); // 晓笑
// 3.
Boy boy2 = new Boy(new Girl("莉莉"));
String girlName22 = getGirlName2(boy2);
System.out.println(girlName22); // 莉莉
}
}
问题
静态代理:实现 Runnable接口的方法创建多线程,就是静态代理模式
Class MyThread implements Runnable {} // 被代理类
Class Thread implements Runnable {} // 代理类
main () {
MyThread t = new MyThread();
Thread th = new Thread(T);
th.start(); // 启动线程,调用线程的 run()
}
问题:
- 代理类和被代理类在编译期就确定了,不利于程序的扩展
- 每个代理类只能为一套接口服务,使得程序开发过程中代理类过多
动态代理
- 通过 Proxy.newProxyInstance()实现动态的创建一个 代理类
- 通过 InvocationHandler接口实现类及其方法 invoke()动态的调用被代理类的方法