1. Java 8新特性:
- Java 8相比Java 7 运行速度更快;
- Java 8新增lambda表达式,使代码更加简洁;
- 新增了Stream API;
- 便于并行;
- 最大化的减少了空指针异常。
2. Lambda表达式:
1. 为什么使用lambda表达式:
lambda是一个匿名函数,我们可以把lambda表达式理解为可以传递的代码(将代码像数据一样传递)。使用lambda表达式可以写出更加简介、更加灵活的代码。使用lambda表达式,可以使Java语言的表达能力得到提升。
package lambda;
/**
* @author feng
* @create 2021-04-04 9:36
*/
public class Employee {
private String name;
private int age;
private double salary;
public Employee() {
}
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
package lambda;
/**
* @author feng
* @create 2021-04-04 10:35
*/
@FunctionalInterface
public interface MyPredicate<T> {
boolean test(T t);
}
package lambda;
/**
* @author feng
* @create 2021-04-04 10:38
*/
public class FilterEmployeesByAge implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getAge() > 35;
}
}
package lambda;
/**
* @author feng
* @create 2021-04-04 11:17
*/
public class FilterEmployeeBySalary implements MyPredicate<Employee> {
@Override
public boolean test(Employee employee) {
return employee.getSalary() > 10000;
}
}
package lambda;
import org.junit.Test;
import java.util.*;
/**
* @author feng
* @create 2021-04-04 9:01
*/
public class LambdaTest {
List<Employee> employees = Arrays.asList(
new Employee("张三",30,5000),
new Employee("李四",35,15000),
new Employee("王五",40,25000),
new Employee("赵六",10,500),
new Employee("天琪",20,8000),
new Employee("老八",50,50000)
);
/**
* 匿名内部类
*/
@Test
public void test1(){
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello world!");
}
};
r1.run();
Runnable b2 = () -> System.out.println("hello world");
b2.run();
}
@Test
public void test2(){
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
TreeSet<Integer> treeSet = new TreeSet<>(comparator);
Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
TreeSet ts2 = new TreeSet(com);
}
@Test
public void test3(){
List<Employee> list = filterEmployees1(this.employees);
for (Employee e : list)
System.out.println(e);
System.out.println("==========================================");
List<Employee> list1 = filterEmployees2(employees);
for (Employee e : list1)
System.out.println(e);
}
//获取年龄大于35的员工
public List<Employee> filterEmployees1(List<Employee> list){
List<Employee> emps = new ArrayList<>();
for (Employee employee : list){
if (employee.getAge() > 35)
emps.add(employee);
}
return emps;
}
/**
* 获取工资大于5000的员工
* @param list
* @return
*/
public List<Employee> filterEmployees2(List<Employee> list){
List<Employee> emps = new ArrayList<>();
for (Employee e : list){
if (e.getSalary() > 5000)
emps.add(e);
}
return emps;
}
/**
* 对上面的条件查询方法进行优化
* 优化方式1:策略模式,提供统一的接口,每一种查询条件提供一个实现类
* 优化方式2:匿名内部类
* 优化方式3:lambda表达式
* 优化方式4: Stream API
*/
/**
* 提供统一的方法进行过滤,查询员工
* @param list
* @param mp
* @return
*/
public List<Employee> filterEmployees(List<Employee> list,MyPredicate<Employee> mp){
List<Employee> emps = new ArrayList<>();
for (Employee e : list)
if (mp.test(e))
emps.add(e);
return emps;
}
@Test
public void test4(){
List<Employee> list = filterEmployees(employees, new FilterEmployeesByAge());
for (Employee employee : list)
System.out.println(employee);
System.out.println("==============================");
List<Employee> list2 = filterEmployees(employees, new FilterEmployeeBySalary());
for (Employee employee : list2)
System.out.println(employee);
}
/**
* 匿名内部类
*/
@Test
public void test5(){
List<Employee> list = filterEmployees(this.employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getAge() > 40;
}
});
for (Employee employee : list)
System.out.println(employee);
System.out.println("-------------------------------------------");
List<Employee> list1 = filterEmployees(this.employees, new MyPredicate<Employee>() {
@Override
public boolean test(Employee employee) {
return employee.getSalary() > 20000;
}
});
for (Employee employee : list1)
System.out.println(employee);
}
/**
* lambda表达式
*/
@Test
public void test6(){
List<Employee> list = filterEmployees(this.employees, (e) -> e.getAge() < 30);
list.forEach(System.out::println);
System.out.println("------------------------------------------");
List<Employee> list1 = filterEmployees(this.employees, (e) -> e.getSalary() < 30000);
list1.forEach(System.out::println);
}
/**
* Stream API
*/
@Test
public void test7(){
employees.stream()
.filter((e) -> e.getSalary() > 5000)
.limit(2)
.forEach(System.out::println);
}
}
2. lambda表达式的语法:
lambda表达式在Java中引入了一个新的语法元素和操作符"->",该操作符被称为lambda操作符或箭头操作符,将lambda表达式分为两个部分:
左侧:指定了lambda表达式的需要的所有参数
右侧:指定了lambda体,即lambda表达式需要执行的功能
- 语法格式一:无参,无返回值,lambda体只需要一条语句
() -> lambda体
@Test public void test2(){ Runnable r = () -> System.out.println("hello world"); r.run(); }
- 语法格式二:有一个参数,没有返回值
(x) -> lambda体
@Test public void test3(){ Consumer<String> con = (x) -> System.out.println(x); con.accept("hello Consumer"); }
- 有一个参数,没有返回值,参数中的小括号可以省略
x -> lambda体
@Test public void test4(){ Consumer<String> con = x -> System.out.println(x); con.accept("hello Consumer"); }
- 有两个参数,有返回值,并且lambda体有多条语句
@Test
public void test5(){
Comparator<Integer> comparator = (x,y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
}
- 有两个参数,有返回值,lambda体只有一条语句,则大括号和返回语句都可以省略
@Test public void test6(){ Comparator<Integer> comparator = (x,y) -> Integer.compare(x, y); }
- lambda表达式中的参数列表的数据类型可以省略不写,因为jvm编译器可以根据上下文进行推断
(Integer x,Integer y) -> Integer.compare(x,y);
##3.函数式接口
lambda表达式需要函数式接口的支持。
接口中只有一个抽象函数的接口是函数式接口(除去抽象函数还可以有默认函数,但是抽象函数的个数必须是一个)。
函数式接口可以用@FunctionalInterface注释,@FunctionalInterface注释可以用于检查接口是否是函数式接口。
package lambda;
/**
* @author feng
* @create 2021-04-04 18:42
*/
@FunctionalInterface
public interface MyFunction<Integer> {
Integer getValue(Integer t);
}
/**
* 对一个整数进行运算
* @param num
* @param my
* @return
*/
public Integer operation(Integer num,MyFunction<Integer> my){
return my.getValue(num);
}
@Test
public void test7(){
Integer operation = operation(100, x -> x * x);
System.out.println(operation);
System.out.println("-----------------");
Integer operation1 = operation(100, x -> x + 200);
System.out.println(operation1);
}
4. Java 8内置的四大核心函数式接口
-
消费型接口:
Consumer<T>: void accept(T t);
/** * 消费型接口测试 * @param money * @param consumer */ public void happy(double money, Consumer<Double> consumer){ consumer.accept(money); } @Test public void test1(){ happy(10000.0, x -> System.out.printf("消费了:%f元",x)); }
-
供给型接口:
Supplier<T>: T get();
/** * 测试供给型接口 * 获取指定个数的数据,并将其放入集合中 * @param num * @param supplier * @return */ public List<Integer> getNumList(int num, Supplier<Integer> supplier){ List<Integer> list = new ArrayList<>(); for (int i = 0; i < num; i++) { Integer integer = supplier.get(); list.add(integer); } return list; } @Test public void test2(){ List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 1000)); for (Integer integer : numList) System.out.println(integer); }
-
函数式接口:
Function<T,R>: R apply(T t);
/** * 函数型接口测试 * 用于处理字符串 * @param string * @param fun * @return */ public String strHandle(String string, Function<String,String> fun){ String apply = fun.apply(string); return apply; } @Test public void test3(){ String handle = strHandle("\t\t\t\t\t我们大家都是地球人。。。", (str) -> str.trim()); System.out.println(handle); }
-
断言型接口:
Predicate<T>: boolean test(T t);
/** * 断言型接口测试 * 筛选满足于指定条件的字符串,并将其添加到集合中 * @param list * @param predicate * @return */ public List<String> filterString(List<String> list, Predicate<String> predicate){ List<String> stringList = new ArrayList<>(); for (String string : list){ if(predicate.test(string)) stringList.add(string); } return stringList; } @Test public void test4(){ List<String> asList = Arrays.asList("hello", "adajksfjs", "dfg", "frgdf", "ee"); List<String> strings = filterString(asList, str -> str.length() > 3); for (String string : strings) System.out.println(string); }
5. 其他内置接口
BiFunction(T,U,R) : R apply(T t,U u);
UnaryOperation(T t) : T apply(T t)
BinaryOperation<T> : T apply(T t1, T t2)
BiConsumer<T,U> : void accept(T t,U u)
- 分别计算int long double 值的函数
- ToIntFunction
- ToLongFunction
- ToDoubleFunction
- 参数分别为int long double的函数
- IntFunction
- LongFunction
- DoubleFunction
6. 方法引用
在lambda表达式中,如果lambda体的实现其他的方法已经实现,那么就可以使用方法引用。可以理解为方法引用是lambda表达式的另外一种表现形式。
方法引用主要有以下三种表现形式:
- 对象:实例方法名
/** * 方法引用 */ @Test public void test1(){ //使用lambda表达式 PrintStream ps = System.out; Consumer<String> con = x -> ps.println(x); con.accept("hello"); Employee employee = new Employee(); Supplier<String> sup = () -> employee.getName(); String str = sup.get(); System.out.println(str); //使用方法引用 PrintStream ps2 = System.out; Consumer<String> con1 = ps2::println; con1.accept("fdgfbgf"); Supplier<Integer> sup1 = employee::getAge; Integer integer = sup1.get(); System.out.println(integer); }
- 类名:静态方法名
/** * 类名:静态方法 */ @Test public void test2(){ Comparator<Integer> com = (x,y) -> Integer.compare(x, y); Comparator<Integer> com1 = Integer::compare; }
- 类名:实例方法名
lambda参数列表中,第一个参数是实例方法的调用者,第二个参数是实例方法的参数时,可以使用ClassName::method
/**
* 类名:实例方法
*/
@Test
public void test3(){
BiPredicate<String,String> bp = (x,y) -> x.equals(y);
BiPredicate<String,String> bp1 = String::equals;
}
lambda体中方法的参数列表与返回类型,必须与函数接口中抽象方法的参数列表返回类型一致。
7. 构造器引用
格式:ClassName::new
需要调用的构造器的参数列表要与函数接口中的抽象方法的参数列表一致。
/**
* 构造器引用
*/
@Test
public void test4(){
//使用lambda
Supplier<Employee> sup = () -> new Employee();
Employee employee = sup.get();
System.out.println(employee);
Function<String,Employee> f1 = (x) -> new Employee(x);
Employee employee2 = f1.apply("zhangsan");
System.out.println(employee2);
//使用构造器调用
Supplier<Employee> sup1 = Employee::new;
Employee employee1 = sup1.get();
System.out.println(employee1);
Function<String,Employee> f2 = Employee::new;
Employee lisi = f2.apply("lisi");
System.out.println(lisi);
}
8. 数组引用
格式: Type[]::new
/**
* 数组引用
*/
@Test
public void test5(){
Function<Integer,String[]> fun = x -> new String[x];
String[] apply = fun.apply(10);
System.out.println(apply.length);
Function<Integer,String[]> fun1 = String[]::new;
String[] apply1 = fun1.apply(20);
System.out.println(apply1.length);
}
3. Stream API
Stream是Java 8中处理集合的关键的抽象概念,它可以指定你对数据希望进行的操作,可以执行非常复杂的过滤、查找、映射数据等操作,使用stream API对数据进行操作,就像使用SQL对数据库进行查询。也可以使用stream API来进行并行操作。简而言之,stream API提供了一种高效而且易于使用的数据处理方式。
Stream是数据渠道,用于操作数据源(集合、数组等)所生产的元素序列。
集合讲的是数据,流讲的是运算。
注意:
- Stream 自己不会存储元素;
- Stream不会改变源对象,相反,进行过一系列运算后会生成一个新的Stream;
- Stream操作是延迟执行的,这意味着Stream操作会等到需要结果的时候才执行。
1. Stream操作的三个步骤
- 创建Stream
/** * 创建Stream */ @Test public void test1(){ //1.可以通过Collection系列提供的stream()或者parallelStream()创建 List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); //2.通过Arrays的静态方法stream()获取数组流 Employee[] employees = new Employee[10]; Stream<Employee> stream1 = Arrays.stream(employees); //3.通过Stream类中的静态方法of() Stream<String> stream2 = Stream.of("sfdg", "dghtj", "hello"); //4.创建无限流 //迭代 Stream<Integer> stream3 = Stream.iterate(1, x -> x + 2); stream3.limit(5) .forEach(System.out::println); //生成 Stream.generate(() -> Math.random()) .limit(10) .forEach(System.out::println); }
- 中间操作
多个中间操作可以连起来形成一个流水线,除非执行终止操作,否则中间操作不会执行任何操作,而在终止操作时一次性全部处理,称为惰性求值。
- 流中的切片与筛选:
/**
* 中间操作
*/
@Test
public void test2(){
employees.stream()
.filter(e -> e.getAge() > 35)
.forEach(System.out::println);
System.out.println("----------------------------------------");
employees.stream()
.filter(employee -> {
System.out.println("hello jj");
return employee.getAge() > 35;
})
.forEach(System.out::println);
System.out.println("----------------------------------------");
employees.stream()
.filter(employee -> employee.getSalary() > 500)
.limit(2)
.forEach(System.out::println);
System.out.println("----------------------------------------");
employees.stream()
.filter(employee -> employee.getSalary() > 300)
.skip(2)
.forEach(System.out::println);
}
- 流中的映射方法
/**
* 测试map方法
*/
@Test
public void test3(){
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
list.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
System.out.println("-----------------------------------");
employees.stream()
.map(lambda.Employee::getName)
.forEach(System.out::println);
System.out.println("-----------------------------------");
Stream<Stream<Character>> stream = list.stream()
.map(StreamTest::filterCharacter);
stream.forEach(sm ->
sm.forEach(System.out::println));
System.out.println("-----------------------------------");
list.stream()
.flatMap(StreamTest::filterCharacter)
.forEach(System.out::println);
}
/**
* 取出字符串中的每一个字符,并将字符保存至集合中,转换为流
* @param str
* @return
*/
public static Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
char[] chars = str.toCharArray();
for(Character ch : chars)
list.add(ch);
return list.stream();
}
- 流中的排序方法
/** * 测试排序 * 自然排序: * 定制排序: */ @Test public void test4(){ //自然排序 List<String> list = Arrays.asList("bbb","ddd","aaa","xxx","ccc","ttt"); list.stream() .sorted() .forEach(System.out::println); System.out.println("----------------------------------"); //定制排序 employees.stream() .sorted((e1,e2) ->{ if(e1.getSalary() == e2.getSalary()) return -Integer.compare(e1.getAge(), e2.getAge()); else return Double.compare(e1.getSalary(),e2.getSalary()); }) .forEach(System.out::println); }
- 终止操作
- 查找与匹配
package stream;
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
/**
* 测试Stream流的匹配与查找
* @author feng
* @create 2021-04-05 17:52
*/
public class StreamTest2 {
List<Employee> employees = Arrays.asList(
new Employee("张三",30,5000, Employee.Status.FREE),
new Employee("李四",35,15000,Employee.Status.BUSY),
new Employee("王五",40,25000, Employee.Status.VOCATION),
new Employee("赵六",10,500,Employee.Status.FREE),
new Employee("天琪",20,8000,Employee.Status.BUSY),
new Employee("老八",50,50000,Employee.Status.VOCATION)
);
/**
*测试allMatch()检查是否匹配所有
*测试anyMatch()检查是否至少匹配一个
*测试noneMatch()检查是否没有匹配所有元素
*/
@Test
public void test1(){
boolean b1 = employees.stream()
.allMatch(e -> e.getStatus().equals(Employee.Status.FREE));
System.out.println(b1);
System.out.println("--------------------------------------");
boolean b2 = employees.stream()
.anyMatch(e -> e.getStatus().equals(Employee.Status.FREE));
System.out.println(b2);
System.out.println("--------------------------------------");
boolean b3 = employees.stream()
.noneMatch(e -> e.getStatus().equals(Employee.Status.FREE));
System.out.println(b3);
}
/**
*测试findFirst()返回第一个元素
*测试findAny()返回任意一个元素
*/
@Test
public void test2(){
Optional<Employee> first = employees.stream()
.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(first.get());
System.out.println("-----------------------");
Optional<Employee> any = employees.stream()
.filter(employee -> employee.getStatus().equals(Employee.Status.FREE))
.findAny();
System.out.println(any.get());
}
/**
* 测试 count() 返回流中元素个数
* max() 返回流中最大值
* min() 返回流中最小值
*/
@Test
public void test3(){
long count = employees.stream()
.count();
System.out.println(count);
System.out.println("------------------------------");
Optional<Integer> max = employees.stream()
.map(Employee::getAge)
.max(Integer::compare);
System.out.println(max.get());
System.out.println("------------------------------");
Optional<Double> min = employees.stream()
.map(Employee::getSalary)
.min(Double::compareTo);
System.out.println(min.get());
}
}
- 规约与收集
/** * 规约 */ @Test public void test1(){ List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); Integer reduce = list.stream() .reduce(0, (x, y) -> x + y); System.out.println(reduce); System.out.println("----------------------------------"); Optional<Double> reduce1 = employees.stream() .map(Employee::getSalary) .reduce(Double::sum); System.out.println(reduce1.get()); } /** * 收集 */ @Test public void test2(){ List<String> collect = employees.stream() .map(Employee::getName) .collect(Collectors.toList()); collect.forEach(System.out::println); System.out.println("------------------------------------"); Set<String> collect1 = employees.stream() .map(Employee::getName) .collect(Collectors.toSet()); collect1.forEach(System.out::println); System.out.println("------------------------------------"); HashSet<Integer> collect2 = employees.stream() .map(Employee::getAge) .collect(Collectors.toCollection(HashSet::new)); collect2.forEach(System.out::println); }
- 统计结果
/**
* 统计结果
*/
@Test
public void test3(){
//总数
Long collect = employees.stream()
.collect(Collectors.counting());
System.out.println(collect);
System.out.println("------------------------------------");
//平均值
Double collect1 = employees.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println(collect1);
System.out.println("------------------------------------");
//总和
Double collect2 = employees.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println(collect2);
System.out.println("------------------------------------");
//最大值
Optional<Employee> collect3 = employees.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
System.out.println(collect3.get());
System.out.println("------------------------------------");
//最小值
Optional<Double> collect4 = employees.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compareTo));
System.out.println(collect4.get());
}
- 分组
/**
* 分组
*/
@Test
public void test4(){
Map<Employee.Status, List<Employee>> collect = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus));
System.out.println(collect);
}
- 多级分组
/**
* 多级分组
*/
@Test
public void test5(){
Map<Employee.Status, Map<String, List<Employee>>> collect = employees.stream()
.collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy((e) -> {
if (e.getAge() <= 35)
return "青年";
else if (e.getAge() <= 50)
return "中年";
else
return "老年";
})));
System.out.println(collect);
}
- 分区
/**
* 分区
*/
@Test
public void test6(){
Map<Boolean, List<Employee>> collect = employees.stream()
.collect(Collectors.partitioningBy(e -> e.getAge() > 35));
System.out.println(collect);
}
- 对一组数进行数学运算
/**
* 对一组数进行数学运算
*/
@Test
public void test7(){
DoubleSummaryStatistics collect = employees.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(collect.getMax());
System.out.println(collect.getAverage());
System.out.println(collect.getMin());
}
- 对字符串进行连接
/**
* 对字符串进行连接
*/
@Test
public void test8(){
String collect = employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(","));
System.out.println(collect);
System.out.println("---------------------------------------------");
String collect1 = employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(",", "--", "--"));
System.out.println(collect1);
}