1.参考尚学堂课程
1.
说明:
lambda 表达式有三种表现形式: ①带 -> 的,②方法引用 :: ③构造器引用::
1. 初体验
List<Employee> employees = Arrays.asList(
new Employee("张三", 16, Double.valueOf(90)),
new Employee("李四", 19, Double.valueOf(120)),
new Employee("王五", 25, Double.valueOf(220)),
new Employee("赵六", 30, Double.valueOf(340))
);
employees.stream()
.filter(e -> e.getSalary() < 100)
.limit(1)
.forEach(System.out::println);
// 只取名字
employees.stream()
.map(Employee::getName)
.forEach(System.out::println);
1. lambda 表达式:
格式:
()->{ 方法体}
这个格式是对某个接口的实现:()是函数式接口的参数列表;{ 方法体} 是函数式接口抽象方法的具体实现,如果方法体只有一行,则大括号和return都可以不写;
参数类型是可以不写的,因为 编译器可以根据上下文推断出来,即“类型推断”
如下:
// 因为 泛型 Integer,所以推断出 x,y的类型
Comparator<Integer> tComparator = (x, y) -> {
System.out.println(x+" "+y);
return Integer.compare(x,y);
};
// List的元素是 String,所以推断 ArrayList的元素也是 String
List<String> strings = new ArrayList<>();
左右遇一括号省:-> 参数列表只有一个参数,()可以省略;-> 方法体只有一行代码,{}可以省,而且 return 也可以省略
Consumer consumer1 = (e)->System.out.println("lambda 有参数,无返回值,参数为:"+e) ;
Consumer consumer2 = e->System.out.println("lambda 有参数,无返回值,参数为:"+e) ;
语法如下:
// 语法: ①无参数,无返回值的接口
Runnable runnable = new Runnable() {
@Override
public void run(){
System.out.println("无参数,无返回值");
}
};
runnable.run();
// 改成lambda后
Runnable runnable1 = () -> System.out.println("lambda 无参数,无返回值");
runnable1.run();
// 语法: ②一个参数,无返回值的接口
Consumer consumer1 = (e)->System.out.println("lambda 有参数,无返回值,参数为:"+e) ;
Consumer consumer2 = e->System.out.println("lambda 有参数,无返回值,参数为:"+e) ;
consumer1.accept("hello");//hello 作为参数传给 e,最后 e到达方法体
// 语法: ③多个参数,有返回值 接口 (假如有 多行代码 的实现)Comparator 接口
Comparator<Integer> tComparator = (x, y) -> {
System.out.println(x+" "+y);
return Integer.compare(x,y);
};
// -1
System.out.println(tComparator.compare(2,3));
二: lambda 表达式是对函数式接口进行操作的,所以需要函数式接口的支持
函数式接口:接口中只有一个抽象方法,用注解
@FunctionalInterface修饰的接口就是函数式接口,里面只可以有一个抽象方法
自定义函数式接口
@FunctionalInterface
public interface MyCustomFunctionInterface<Integer> {
int getValue(Integer t);
}
public int getSum(Integer x,MyCustomFunctionInterface<Integer> myInterface){
return x + myInterface.getValue(x);
}
// Integer 必须加上,不然推断不出 x的类型
MyCustomFunctionInterface<Integer> fi = (x)->{
return 1+x;
};
int value = fi.getValue(10);
System.out.println(value);
// 函数式接口最常见的作用是作为一个方法的参数
int sum = getSum(1, i -> i + 1);
// sum 3
System.out.println(sum);
函数式接口练习:
// 1
List<Employee> employees = Arrays.asList(
new Employee("张三", 16, Double.valueOf(90)),
new Employee("李四", 19, Double.valueOf(120)),
new Employee("王五", 25, Double.valueOf(220)),
new Employee("赵六", 30, Double.valueOf(340))
);
Collections.sort(employees,(x,y)->{
if (x.getAge() - y.getAge() == 0) {
//compareTo()用于比较字符串大小 0 为相等;负为 x < y
return x.getName().compareTo(y.getName());
}
// 如果降序 return -(x.getAge() - y.getAge());
return x.getAge() - y.getAge();
});
employees.stream()
.forEach(System.out::println);
// 3
// 不是很符合要求
MyCustomFunctionInterface<Long,Long> fi1 = (x,y)->{
Long a = x+y;
return a;
};
System.out.println(fi1.getValue(1L,2L));
MyCustomFunctionInterface<Long,Long> fi2 = (x,y)->{
Long b = x*y;
return b;
};
System.out.println(fi2.getValue(1L,2L));
// 这个是最满足要求的
System.out.println(getLongSum(1L,2L,(x,y)->x + y));
System.out.println(getLongSum(1L,2L,(x,y)->x * y));
public Long getLongSum(Long x,Long y,MyCustomFunctionInterface<Long,Long> fi){
// 函数式接口 作为形参,接口抽象方法需要的实参通过getLongSum 这个方法传递
return fi.getValue(x,y);
}
新特性:内置的四大核心函数式接口
① Consumer<T> ---> void accept(T t); 消费型
② Supplier<T> ---> T get(); 供给型(有返回值)
③ Function<T, R> ---> R apply(T t); 函数型(有返回值)
④ Predicate<T> ---> boolean test(T t); 断言型 (返回值是booleam)
简单例子:
@Test
public void testGetAcccountById() {
consumerTest(10,e-> System.out.println(e));
supplierTest(0,()->{
return 1;
});
Integer integer = functionTest(10, e -> e + 10);
predicateTest(10,e->10>20);
}
public void consumerTest(Integer a,Consumer<Integer> consumer){
// 函数式接口接受参数,具体操作看lambda 方法体
consumer.accept(a);
}
public Integer supplierTest(Integer x, Supplier<Integer> supplier){
return supplier.get();
}
public Integer functionTest(Integer x, Function<Integer, Integer> functon){
return functon.apply(x);
}
public boolean predicateTest(Integer x, Predicate<Integer> predicate){
return predicate.test(x);
}
使用:
@Test
public void returnTest() {
System.out.println(" returnTest main thread ......"+Thread.currentThread().getName());
Integer result = getReturn(() -> {
System.out.println("aaaaa");
System.out.println("hhhhhh");
return 1;
});
System.out.println(result);
System.out.println(result);
}
public <U> U getReturn(Supplier<U> supplier){
return supplier.get();
}
@Test
public void returnTest2() {
System.out.println(" returnTest2 main thread ......"+Thread.currentThread().getName());
// Consumer 的 accept 消费一个对象(业务逻辑)
Consumer<Integer> consumer = new Consumer<Integer>() {
// 不带有返回值
@Override
public void accept(Integer integer) {
System.out.println(integer);
System.out.println("aaaaa");
System.out.println("hhhhhh");
}
};
// 开始调用 目标方法
consumer.accept(1);
}
四:方法引用和构造器引用
方法引用:如果lambda表达式方法体已经被实现了,那么可以用方法引用(lambda 表达式的另外一种表现形式,这种形式都不需要 ->)
ps:lambda 表达式表现形式:要么自己写方法体,要么引用已经被实现的方法
举例:
Employee employee = new Employee("张三",10,Double.valueOf(10));
Supplier supplier = () -> employee.getName();
System.out.println(supplier.get());
// 方法引用是lambda表达式的第二种 表现形式(这个时候 -> 没了)
Supplier<String> supplier2 = employee::getName;
System.out.println(supplier2.get());
语法主要三种:(因为语法规定了是 方法名,所以不需要加 括号)
实例对象::实例方法名
类::静态方法名
类::实例方法名
要求:
要去引用的方法参数列表 和返回值类型要和 函数式接口的 抽象方法的参数列表和返回值类型一样
如果lambda表达式第一个参数是实例方法调用者,第二个参数是实例方法参数,可以用 类::实例方法名
举例说明:
Consumer consumer = x -> System.out.println(x);
consumer.accept(10);
// System 类里定义了 public final static PrintStream out = null;
PrintStream ps = System.out;
Consumer consumer2 = ps::println;
consumer2.accept(10);
类::静态方法
// lambda 表达式第一种形式 ->
Comparator<Integer> comparator1 = (x, y) -> Integer.compare(x, y);
comparator1.compare(1,2);
//lambda 表达式第二种形式 类::静态方法名
Comparator<Integer> comparator2 = Integer::compare;
comparator2.compare(1,2);
类::实例方法名
如果lambda表达式第一个参数是实例方法调用者,第二个参数是实例方法参数,可以用 类::实例方法名
// lambda 表达式第一种形式 ->
BiPredicate<String,String> predicate = (x, y) -> x.equals(y);
System.out.println(predicate.test("1","1"));;
//lambda 表达式第二种形式 类::对象方法名
BiPredicate<String,String> predicate2 = String::equals;
System.out.println(predicate2.test("1","2"));
构造器引用:
语法:
类名::new
// lambda 第一种表现方式
Supplier supplier = () -> new Employee();
// 获取对象
supplier.get();
// lambda 第三种表现方式 类名::new
Supplier supplier2 = Employee::new;
supplier2.get();
数组引用:
Type::new
四 强大的Stream
映射操作:
map(Function) 参数是函数型接口,传一个参数,返回一个结果,将流中的每个元素都通过函数式接口的抽象方法处理,并返回一个新的元素,最终这些新的元素组成新的流,最终流里的元素类型就是刚刚返回的新元素类型
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
flatMap(Function) 参数是函数型接口,传一个T,返回的结果为子Stream:将流中的每一个元素换成另一个流,最后把所有的流连接成新的流
(ps:关键字连接 类似于 化零为整)
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
如下:
//提供一个将 String 转化为 Stream<Charater> 的方法
/**
* @Description: 要求返回时 Stream流,则考虑 list.stream();Arrays.stream(包装类型的数组);Stream.of包装类型的数组)
*
*/
public static Stream<Character> getCharacterStream(String str){
// 转成list,最后返回list.stream()
List<Character> chars = new ArrayList<>();
// 转成包装类型的数组,最后返回 Arrays.stream(包装类型的数组)or Stream.of包装类型的数组)
Character[] charcterArray = new Character[9];
char[] charAyyar = str.toCharArray();
for (int a = 0; a < charAyyar.length;a++) {
// 将基本类型的转为 包装类型,这样才可以转成stream
chars.add(charAyyar[a]);
charcterArray[a] = charAyyar[a];
}
return chars.stream();
// stream 参数为数组,这个数组要求是 包装类型的,不可以 基本类型
// return Arrays.stream(charcterArray);
// return Stream.of(charcterArray);
}
// 以下是测试代码
List<String> strList = Arrays.asList("aaa","bbb","ccc");
// a
// a
// a
getCharacterStream("aaa").forEach(System.out::println);
// java.util.stream.ReferencePipeline$Head@318ba8c8
// java.util.stream.ReferencePipeline$Head@6dbb137d
// java.util.stream.ReferencePipeline$Head@3c9d0b9d
// 发现打印结果是个流对象,因为 map 将 "aaa" 转化为 Stream<Character> (也就是 getCharacterStream(String str) 返回值)
strList.stream()
.map(CodeTest::getCharacterStream)
.forEach(System.out::println);
// 以上代码 可以这样理解
Stream<Stream<Character>> strmap = strList.stream()
.map(CodeTest::getCharacterStream);
strmap.forEach(System.out::println);
// 用 map 遍历出 a a a b b b c c c
strList.stream()
.map(CodeTest::getCharacterStream)
// 经过map之后的所有元素都是 Stream,所以 e 就是个Stream
.forEach(e -> e.forEach(System.out::println));
// 说明 map 返回是Stream,getCharacterStream 对 strList 每个元素操作后,将这个元素转成了 Stream,这个Stream 里元素是 Character
// 数据结构类似:{{a,a,a},{b,b,b},{c,c,c}}
// 用 flatMap 遍历出 a a a b b b c c c
strList.stream()
.flatMap(CodeTest::getCharacterStream)
.forEach(System.out::println);
// 以上代码等价于
strList.stream()
.flatMap(e->{
return getCharacterStream(e);
})
.forEach(System.out::println);
题外话:
char[] chars = "abc".toCharArray();
for (char c : chars) {
// a
// b
// c
System.out.println(c);
}
char[] chars2 = "张三".toCharArray();
for (char c : chars2) {
// 张
// 三
System.out.println(c);
}
说明:
此例中map和flatMap 的区别:
这个从函数式接口抽象方法需要的参数类型就可看出来:map 参数列表是T,返回的是R,flatMap 参数列表是T,返回的是Stream
其次,map 返回的Stream<R> 最终流里的元素 是R,是对每个元素操作后返回的R,进行合并
flatMap 返回的是 Stream<Stream<R>>,最终流里的元素首先是 Stream<R>,然后对每个元素操作后返回的Stream<R> 里面的R 进行合并
简单一句话:map 最终合并的是 每个元素进行操作后的返回值;flatMap 最终合并的是 每个元素进行操作后返回的Stream<R> 里的元素
类似于:
//map 模拟
List<List> lastList = new ArrayList<>();
// 模拟返回的第一个流
List<String> oneList = new ArrayList<>();
oneList.add("a");
oneList.add("b");
oneList.add("c");
// 模拟返回的第二个流
List<String> twoList = new ArrayList<>();
twoList.add("aa");
twoList.add("bb");
twoList.add("cc");
// 模拟返回的第三个流
List<String> threeList = new ArrayList<>();
threeList.add("aaa");
threeList.add("bbb");
threeList.add("ccc");
// 将这三个流连接成一个新的流(关键字:连接)
lastList.add(oneList);
lastList.add(twoList);
lastList.add(threeList);
lastList.stream()
.forEach(System.out::println);
System.out.println("我是快乐的分割线----------------------------------------------------");
lastList.stream()
.forEach(e->e.stream().forEach(System.out::println));
结果为:
[a, b, c]
[aa, bb, cc]
[aaa, bbb, ccc]
我是快乐的分割线----------------------------------------------------
a
b
c
aa
bb
cc
aaa
bbb
ccc
//flatMap 模拟
List<String> lastList = new ArrayList<>();
// 模拟返回的第一个流
List<String> oneList = new ArrayList<>();
oneList.add("a");
oneList.add("b");
oneList.add("c");
// 模拟返回的第二个流
List<String> twoList = new ArrayList<>();
twoList.add("aa");
twoList.add("bb");
twoList.add("cc");
// 模拟返回的第三个流
List<String> threeList = new ArrayList<>();
threeList.add("aaa");
threeList.add("bbb");
threeList.add("ccc");
// 将这三个流连接成一个新的流(关键字:连接)
lastList.addAll(oneList);
lastList.addAll(twoList);
lastList.addAll(threeList);
lastList.stream()
.forEach(System.out::println);
结果为:
a
b
c
aa
bb
cc
aaa
bbb
ccc
排序:
List<Employee> employees = Arrays.asList(
new Employee("张三", 30, Double.valueOf(90)),
new Employee("李四", 19, Double.valueOf(120)),
new Employee("王五", 25, Double.valueOf(220)),
new Employee("赵六", 1, Double.valueOf(340))
);
Stream<String> a = Stream.of("a", "c", "d", "b");
// 自然排序
a.sorted().forEach(System.out::println);
// 自定义排序规则
employees.stream().sorted((x,y)->x.getAge()-y.getAge()).forEach(System.out::println);
employees.stream().sorted((x,y)->x.getName().compareTo(y.getName())).forEach(System.out::println);
终止操作:
reduce:归约操作
可以将流中元素反复结合起来,得到一个值
①
Optional<T> reduce(BinaryOperator<T> accumulator);
②
T reduce(T identity, BinaryOperator<T> accumulator);
参数说明:
BinaryOperator<T> 的父接口 ---> BiFunction<T, U, R> ---> R apply(T t, U u);
③
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
代码如下:
List<Integer> list = Arrays.asList(1,2,3);
Integer reduce = list.stream()
.reduce(0, (x, y) -> x + y);
// 6 说明:0 作为identity ,x 为 0 y 为 1 ,0 + 1 作为新的x ,y为 2 ,0 + 1 + 2 作为新的x,y 为3
System.out.println(reduce);
List<Employee> list = Arrays.asList(
new Employee("张三",10,Double.valueOf(100)),
new Employee("李四",20,Double.valueOf(200)),
new Employee("王五",30,Double.valueOf(300))
);
//集合中员工总薪资 Stream 中没有 sum方法
Optional<Double> max = list.stream()
.map(Employee::getSalary)
// Optional<T> max(Comparator<? super T> comparator);
.max(Double::compareTo);
// Optional[300.0]
System.out.println(max);
// 300.0
System.out.println(max.get());
Optional<Double> min = list.stream()
.map(Employee::getSalary)
// Optional<T> min(Comparator<? super T> comparator);
.min((x,y)->{
return (BigDecimal.valueOf(x).intValue() - (BigDecimal.valueOf(y)).intValue());
} );
// Optional[100.0]
System.out.println(min);
// 100.0
System.out.println(min.get());
// 薪资求和
Optional<Double> sumOpt = list.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
// Optional[600.0]
System.out.println(sumOpt);
// 600.0
System.out.println(sumOpt.get());
收集器:Stream的 collect
Collectors 这个工具类提供了很多静态方法
①转为list,set集合
List<Employee> list = Arrays.asList(
new Employee("张三",10,Double.valueOf(100)),
new Employee("张三",10,Double.valueOf(100)),
new Employee("李四",20,Double.valueOf(200)),
new Employee("王五",30,Double.valueOf(300))
);
List<Employee> collect = list.stream().collect(Collectors.toList());
collect.stream().forEach(System.out::println);
System.out.println("----------------------------我是快乐的分割线----------------------------");
// 去重,将元素放到set集合里
Set<Employee> collect1 = list.stream().collect(Collectors.toSet());
collect1.stream().forEach(System.out::println);
结果如下
Employee{name='张三', age=10, salary=100.0}
Employee{name='张三', age=10, salary=100.0}
Employee{name='李四', age=20, salary=200.0}
Employee{name='王五', age=30, salary=300.0}
----------------------------我是快乐的分割线----------------------------
Employee{name='张三', age=10, salary=100.0}
Employee{name='王五', age=30, salary=300.0}
Employee{name='李四', age=20, salary=200.0}
//自定义java类放到set里要重写equals和hashCode
package com.example.demo;
import java.math.BigDecimal;
import java.util.Objects;
/**
* @program: springboot_01
* @description:
* @author: guoyiguang
* @create: 2021-01-09 21:54
**/
public class Employee {
private String name;
private Integer age;
private Double salary;
public Employee() {
}
public Employee(String name, Integer 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 Integer getAge() {
return age;
}
public void setAge(Integer 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 +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(name, employee.name) &&
Objects.equals(age, employee.age) &&
Objects.equals(salary, employee.salary);
}
@Override
public int hashCode() {
return Objects.hash(name, age, salary);
}
}
②
Collectors 的 toCollection 方法指定返回的集合类型
Collector<T, ?, C> toCollection(Supplier<C> collectionFactory)
List<Employee> list = Arrays.asList(
new Employee("张三",10,Double.valueOf(100)),
new Employee("张三",10,Double.valueOf(100)),
new Employee("李四",20,Double.valueOf(200)),
new Employee("王五",30,Double.valueOf(300))
);
// 指定返回集合
// 指定 HashSet
HashSet<Employee> collect = list.stream()
//ollectors.toCollection(Supplier<T>) 形参是提供型接口
.collect(Collectors.toCollection(() -> new HashSet<>()));
collect.stream().forEach(System.out::println);
System.out.println("------------------------------------------------");
// 等价于下面写法 new 一个对象可以考虑构造器引用
HashSet<Employee> collect2 = list.stream()
//ollectors.toCollection(Supplier<T>) 形参是提供型接口
.collect(Collectors.toCollection(HashSet::new));
collect2.stream().forEach(System.out::println);
结果如下:
Employee{name='张三', age=10, salary=100.0}
Employee{name='王五', age=30, salary=300.0}
Employee{name='李四', age=20, salary=200.0}
------------------------------------------------
Employee{name='张三', age=10, salary=100.0}
Employee{name='王五', age=30, salary=300.0}
Employee{name='李四', age=20, salary=200.0}
------------------------------------------------
Employee{name='张三', age=10, salary=100.0}
Employee{name='张三', age=10, salary=100.0}
Employee{name='李四', age=20, salary=200.0}
Employee{name='王五', age=30, salary=300.0}
③
Collectors 里提供静态的 总数,求和,平均值,最大,最小
Collectors.summingDouble 求和的
④
Collectors 里提供静态的 summarizingDouble 概要,返回的值是DoubleSummaryStatistics 类型的,可以拿到 double类型的 总和,总数量,最大,最小,平均值
③和④的代码如下:
List<Employee> list = Arrays.asList(
new Employee("张三",10,Double.valueOf(100)),
new Employee("张三",10,Double.valueOf(100)),
new Employee("李四",20,Double.valueOf(200)),
new Employee("王五",30,Double.valueOf(300))
);
// ③Collectors 里提供静态的 总数,平均值,最大,最小
Long count = list.stream()
// 底层是归约算的 :reducing(0L, e -> 1L, Long::sum);
.collect(Collectors.counting());
System.out.println(count);
long count1 = list.stream()
.count();
System.out.println(count1);
Double sum = list.stream()
.collect(Collectors.summingDouble(e -> e.getSalary()));
// 700.0
System.out.println(sum);
Double ave = list.stream()
.collect(Collectors.averagingInt(e -> e.getSalary().intValue()));
System.out.println(ave);
Optional<Double> max1 = list.stream()
.map(Employee::getSalary)
// Comparator<? super T> comparator
.collect(Collectors.maxBy(Double::compareTo));
// 300.0
System.out.println(max1.get());
Optional<Employee> min1 = list.stream()
// 不做映射处理,x,y 就是 Employee
// .map(Employee::getSalary)
.collect(Collectors.minBy((x, y) -> (BigDecimal.valueOf(x.getSalary()).subtract(BigDecimal.valueOf(y.getSalary()))).intValue()));
// Employee{name='张三', age=10, salary=100.0} ;如果上面做了 .map(Employee::getSalary) 处理,拿到的是 100.0
System.out.println(min1.get());
// ④Collectors 里提供静态的 summarizingDouble 概要,返回的值是DoubleSummaryStatistics 类型的,可以拿到 double类型的 总和,总数量,最大,最小,平均值
DoubleSummaryStatistics collect = list.stream()
.map(Employee::getSalary)
// summarizingDouble(ToDoubleFunction<? super T> mapper) 是函数型接口
.collect(Collectors.summarizingDouble(x -> x));
System.out.println(collect.getCount());
System.out.println(collect.getSum());
System.out.println(collect.getMax());
System.out.println(collect.getMin());
System.out.println(collect.getAverage());
⑤ Collectors 的分组函数(有三个)
public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,Supplier<M> mapFactory,Collector<? super T, A, D> downstream)
代码如下:
List<Employee> list = Arrays.asList(
new Employee("张三",10,Double.valueOf(100)),
new Employee("张三",15,Double.valueOf(150)),
new Employee("李四",20,Double.valueOf(200)),
new Employee("王五",30,Double.valueOf(300))
);
// 分组
Map<String, List<Employee>> collect = list.stream()
// public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier)
// Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream)
// public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,Supplier<M> mapFactory,Collector<? super T, A, D> downstream)
.collect(Collectors.groupingBy(Employee::getName));
System.out.println(collect);
// 多级分组
Map<String, Map<Integer, List<Employee>>> collect1 = list.stream()
.collect(Collectors.groupingBy(Employee::getName, Collectors.groupingBy(e -> e.getAge())));
⑥Collectors 分区
Collectors.partitioningBy
List<Employee> list = Arrays.asList(
new Employee("张三",10,Double.valueOf(100)),
new Employee("张三",15,Double.valueOf(150)),
new Employee("李四",20,Double.valueOf(200)),
new Employee("王五",30,Double.valueOf(300))
);
// 分区
Map<Boolean, List<Employee>> collect = list.stream()
// Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate)
.collect(Collectors.partitioningBy(e -> e.getSalary().intValue() < 200));
System.out.println(collect);
打印结果格式化后:
⑦Collectors.join 拼接,要求是字符串
List<Employee> list = Arrays.asList(
new Employee("张三",10,Double.valueOf(100)),
new Employee("张三",15,Double.valueOf(150)),
new Employee("李四",20,Double.valueOf(200)),
new Employee("王五",30,Double.valueOf(300))
);
String collect = list.stream()
.map(Employee::getName)
// public static Collector<CharSequence, ?, String> joining()
.collect(Collectors.joining());
// 张三张三李四王五
System.out.println(collect);
String collect2 = list.stream()
.map(Employee::getName)
// public static Collector<CharSequence, ?, String> joining()
.collect(Collectors.joining(","));
// 张三,张三,李四,王五
System.out.println(collect2);
Optional类
代表一个值存在不存在,可以用来避免空指针
// 创建 Optional(模拟从redis获取数据)
Optional<Employee> employeeOpt = Optional.of(new Employee("王五",30,Double.valueOf(300)));
// 空指针 模拟从数据库查出 Employee,并放到 Optional里
//Employee nullEmp = null;
//Optional.of(nullEmp); // 可以迅速定位 Optional.of(nullEmp) 这出异常了
Optional<Employee> emptyOpt = Optional.empty();
// java.util.NoSuchElementException: No value present
//System.out.println(emptyOpt.get());
Optional<Employee> ofNullOpt= Optional.ofNullable(new Employee("张三", 10, Double.valueOf(100)));
if (employeeOpt.isPresent()) {
// employeeOpt 里有 value 为:Employee{name='王五', age=30, salary=300.0}
//System.out.println(" employeeOpt 里有 value 为:"+employeeOpt.get().toString());
}else{
// 模拟从数据库查数据
//Employee emp = employeeOpt.orElse(new Employee("王五", 30, Double.valueOf(300)));
Employee emp = employeeOpt.orElseGet(Employee::new); // 类似之前的 if else
}
// orElseGet(Supplier<? extends T> other) 容器里有对象就返回,否则就用 Supplier 内部抽象方法实现的对象
Employee employee = emptyOpt.orElseGet(() -> new Employee("李四", 20, Double.valueOf(200)));
Optional<String> s = emptyOpt.map(e -> e.getName());
System.out.println(s.get());
// flatMap(Function<? super T, Optional<U>> mapper) 接受T 返回 Optional
// 获取包装对象里的属性
Optional<String> o = emptyOpt.flatMap(e -> Optional.of(e.getName()));
在代码中如何避免空指针的?
// java 8 之前 获取员工老板姓名
Boss xiaoMa = new Boss("马化腾", 60);
Employee emp = new Employee("张小龙", 50, Double.valueOf(80), xiaoMa);
String boosName ;
if (null != emp) {
if (null != emp.getBoss()) {
boosName = emp.getBoss().getName();
}
}
// java 8 用Optional 代替 null,消除 了 if(null != 对象引用){}else{创建对象}
Optional<Employee> empOpt = Optional.ofNullable(new Employee("张小龙", 50, Double.valueOf(80), null));
// 有就获取没有就创建 , 这样保证了 emp1 肯定不是空的;orElse 也可以达到一样的效果
Employee emp1 = empOpt.orElseGet(() -> new Employee("张小龙(1)", 50, Double.valueOf(80), xiaoMa));
Optional<Boss> bossOpt = Optional.ofNullable(emp1.getBoss());
// 保证了boss不为空
Boss boss = bossOpt.orElseGet(() -> new Boss("马化腾(1)", 60));
// 马化腾(1)
System.out.println(boss.getName());
日期:
LocalDate
LocalDateTime now = LocalDateTime.now();
// 2021-01-10T15:46:52.355
System.out.println(now);
LocalDateTime of = LocalDateTime.of(2021, 1, 10, 15, 40, 50);
// 2021-01-10T15:40:50
System.out.println(of);
LocalDateTime tenYearsLater = now.plusYears(10L);
// 2031-01-10T15:50:24.296
System.out.println(tenYearsLater);
System.out.println("获取年月日小时分钟秒");
System.out.println(now.getYear());
// JANUARY
System.out.println(now.getMonth());
// 10
System.out.println(now.getDayOfMonth());
// 10
System.out.println(now.getDayOfYear());
System.out.println(now.getHour());
System.out.println(now.getMinute());
System.out.println(now.getSecond());
//
LocalDate start = LocalDate.now();
// 2021-01-10
System.out.println(start);
LocalDate end = LocalDate.now();
Period between1 = Period.between(start, end);
// 最少精确到日
System.out.println(between1.getDays());
// 时间戳: 从 1970 年开始到现在的毫秒值
Instant ins = Instant.now();
// 2021-01-10T07:56:10.131Z 时间不对;此刻是 15:58
System.out.println(ins);
OffsetDateTime offsetNow = ins.atOffset(ZoneOffset.ofHours(8));
// 2021-01-10T15:59:24.551+08:00
System.out.println(offsetNow);
Instant ins2 = Instant.now();
Duration between = Duration.between(ins, ins2);
// 时间段差了几毫秒;几天,几小时,几分钟
System.out.println(between.toMillis());
时间矫正器:
// 时间矫正器
LocalDate now = LocalDate.now();
// 2021-01-10
System.out.println(now);
LocalDate with = now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
// 2021-01-17
System.out.println(with);
LocalDateTime nowTime = LocalDateTime.now();
// 2021-01-10T16:15:05.441
System.out.println(nowTime);
LocalDateTime future = nowTime.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
// 2021-01-17T16:14:51.428
System.out.println(future);
DateTimeFormatter 格式化日期:
// 格式化日期 (转成字符串,转成日期)
DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
LocalDateTime now = LocalDateTime.now();
String format = now.format(dtf);
// 2021-01-10T16:20:30.951
System.out.println(format);
DateTimeFormatter dtf2 = DateTimeFormatter.ISO_DATE;
LocalDate nowDate = LocalDate.now();
String format1 = nowDate.format(dtf2);
// 2021-01-10
System.out.println(format1);
// 自定义格式化日期形式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
LocalDateTime nowTime = LocalDateTime.now();
String format2 = nowTime.format(dateTimeFormatter);
// 2021年01月10日 16:23:52
System.out.println(format2);
LocalDateTime parse1 = nowTime.parse(format2,dateTimeFormatter);
// 2021-01-10T16:34:52
System.out.println(parse1);