- 什么是lambda表达式
- 语法格式
- 函数式接口
- 四大内置核心函数式接口
- Consumer
- Supplier
- Function
- Predicate
- 引用
- 方法引用
- 构造器引用
- 数组引用
- stream流
1. 什么是lambda表达式
- 匿名函数
- 更紧凑,简洁的代码风格
(1)原先的写法——匿名内部类
public void test(){
//匿名内部类
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
//调用
TreeSet<Integer> set = new TreeSet<>(comparator);
}
(2)Lambda的写法
public void test(){
// Lambda 表达式
Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);
//调用
TreeSet<Integer> set = new TreeSet<>(comparator);
}
2. lambda表达式的写法
(1)无参数,无返回值
//1.原先写法
Runnable run = new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
};
-----------------------------------------------------
//2.lambda写法
Runnable run = () -> { System.out.println("Hello World");}
(2)有参数,无返回值
//1 该lambda方法定义了一个函数:打印x
Consumer<String> consumer = (x) -> { System.out.println(x); }
//2 执行该函数,x="打印"
consumer.accept("打印");
(3)有参数,有返回值
//1. 该lambda方法定义了一个比较器
Comparator<Integer> comparator = (a, b) -> {
System.out.println("比较接口");
return Integer.compare(a, b);
};
//2. 如果只有一条执行语句,return可以不写
Comparator<Integer> comparator = (a, b) -> { Integer.compare(a, b); };
(4)注意事项
- 若lambda体中只有一条语句,return可以不写
- lambda参数列表的数据类型可以省略不写,因为JVM编译器会自动推断
- eg. (Integer a, Integer b) ->{ ... } 等价于 (a, b) -> { ... }
3. 如何定义函数式接口
- 两点
- 1. 编写一个接口,加上@FunctionalIterface的注解
- 2. 接口里只能写一个抽象方法
(1)定义一个函数式接口
@FunctionalInterface
public interface MyFun {
//定义了一个count函数
Integer count(Integer a, Integer b);
}
(2)使用
public void test(){
//定义count函数
MyFun myFun1 = (a, b) -> a + b;
//执行函数
int x = myFun1.count(1, 2);
//结果为 1+2 = 3
System.out.println(x);
}
(3)更高端点的用法
public Integer operation(Integer a, Integer b, MyFun myFun){
return myFun.count(a, b);
}
public void test(){
Integer result = operation(1, 2, (x, y) -> x + y);
System.out.println(result);
}
4. 四大内置核心函数式接口
- 实际开发中,很少需要自定义函数式接口,JDK已经提供
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
消费型接口:Consumer | 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) |
(1)Consumer
- 源码
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
- 用法
//1. accept()方法
public void test(){
//定义函数
Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
//输出结果为 ”消费型接口100“
consumer.accept(100);
}
//2. andThen()方法,
public static void main(String[] args) {
//consumer1函数
Consumer<Integer> consumer1 = (x) -> {
System.out.println(String.valueOf(x+"_"));
};
//consumer2函数
Consumer<Integer> consumer2 = (y) -> {
System.out.println(String.valueOf(y * 2));
};
//comsumer3函数:先执行consumer1,接着执行consumer2
Consumer<Integer> comsumer3 = consumer1.andThen(consumer2);
//结果为1_2
comsumer3.accept(1);
}
(2)Supplier
- 源码
@FunctionalInterface
public interface Supplier<T> {
T get();
}
- 用法
private static void testSupplier(){
//1.1 无需参数,有返回值
Supplier<Integer> sup1 = () -> { return 1; };
//1.2 打印1
System.out.println(sup1.get());
//2.1 调用String的无参构造器
Supplier<String> sup2 = String::new;
//2.2 打印空字符串“”
System.out.println(sup2.get());
//3.1
Supplier<String> sup3 = ()->{ return new String("123"); };
//3.2 打印字符串“123”
System.out.println(sup3.get());
}
(3)Function<T, R>
- 源码
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
//返回一个先执行before函数对象apply方法再执行当前函数对象apply方法的函数对象
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
//返回一个先执行当前函数对象apply方法再执行after函数对象apply方法的函数对象。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
- 用法
//Function函数
Function<String, String> function = a -> a + " Jack!";
System.out.println(function.apply("Hello")); // Hello Jack!
//andThen()
Function<String, String> function = a -> a + " Jack!";
Function<String, String> function1 = a -> a + " Bob!";
String greet = function.andThen(function1).apply("Hello");
System.out.println(greet); // Hello Jack! Bob!
//compose()
Function<String, String> function = a -> a + " Jack!";
Function<String, String> function1 = a -> a + " Bob!";
String greet = function.compose(function1).apply("Hello");
System.out.println(greet); // Hello Bob! Jack!
(4)Predicate
- 源码
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
//与操作
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
//取反操作
default Predicate<T> negate() {
return (t) -> !test(t);
}
//或操作
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
//是否相等且非空
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
- 用法
private static void testSupplier(){
Integer age = 35;
String gender = "男";
//是否大于等于35岁
Predicate<Integer> predicate1 = (i) -> i >= 35;
//是否是男性
Predicate<Integer> predicate2 = (i) -> i.equals("男");
//返回false
Boolean b1 = predicate1.test(25);
//返回true
Boolean b2 = predicate2.test(“男”)
}
(5)其他常用函数式接口
- 详细用法可以参考这个Reference:https://blog.csdn.net/qq_29848473/article/details/79554472
函数类型 | 函数名 | 作用 |
---|---|---|
Consumer |
|
|
|
| |
|
| |
Supplier |
|
|
|
| |
Predicate |
|
|
|
| |
Function |
|
|
|
| |
|
| |
|
| |
|
| |
|
| |
Operation |
|
|
|
| |
|
|
5. 引用
- 若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”
(1)方法引用
- 对象 :: 实例方法
//1. 对象 :: 实例方法
public void test(){
PrintStream ps = System.out;
//传统lambda函数写法
Consumer<String> con1 = (s) -> ps.println(s);
con1.accept("aaa");
//方法引用的写法
Consumer<String> con2 = ps::println;
con2.accept("bbb");
}
- 类 :: 静态方法
//2. 类 :: 静态方法
public void test(){
Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
System.out.println(com1.compare(1, 2));
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(2, 1));
}
- 类 :: 实例方法
//3. 类 :: 实例方法
public void test03(){
BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);
System.out.println(bp1.test("a","b"));
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("c","c"));
}
(2)构造器引用
@Test
public void test04(){
Supplier<List> sup1 = () -> new ArrayList();
Supplier<List> sup2 = ArrayList::new;
}
(3)数组引用
Type :: new
6. Stream API
(1)什么是流(Stream)
- 用于操作数据源(集合,数组等)所生成的元素序列
- 注意事项
- Stream不会自己存储元素
- Stream不会改变源对象,它们会返回一个持有结果的新stream
- Stream是延迟执行的,这意味着它们会等到需要结果时才执行
(2)Stream的三个步骤
- 1. 创建Stream
- 集合/数组 .stream()方法,生成一个流
- 2. 中间操作
- 比如 filter(), limit(), distinct()等中间链操作
- 3. 终止操作
(3)创建流的几种方法
public void test(){
//集合流
//Collection.stream() 穿行流
//Collection.parallelStream() 并行流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//数组流
//Arrays.stream(array)
String[] strings = new String[10];
Stream<String> stream2 = Arrays.stream(strings);
//Stream 静态方法
//Stream.of(...)
Stream<Integer> stream3 = Stream.of(1, 2, 3);
//无限流
//迭代
Stream<Integer> stream4 = Stream.iterate(0, (i) -> ++i+i++);
stream4.forEach(System.out::println);
//生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
7. stream流的中间操作
(1)筛选 / 切片
- filter(Function):从流中排除某些元素
- limit(n):截断流,只取流的前n个元素
- skip(n):跳过元素,返回一个舍弃了前n个元素的流;若流中元素不足n个,则返回一个空流;与 limit(n) 互补
- distinct():筛选,通过流所生成的 hashCode() 与 equals() 取除重复元素
//构造数据
List<Employee> emps = Arrays.asList(
new Employee(101, "Z3", 19, 9999.99),
new Employee(102, "L4", 20, 7777.77),
new Employee(103, "W5", 35, 6666.66),
new Employee(104, "Tom", 44, 1111.11),
new Employee(105, "Jerry", 60, 4444.44)
);
public void test01(){
emps.stream()
.filter((x) -> x.getAge() > 35)
.limit(3)
.distinct()
.skip(1)
.forEach(System.out::println);
}
(2)映射
- map(Function):映射,接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap(Function):接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
- 注:map和flatMap之间的关系可以类比为 add() 和 addAll()之间的关系
//map
public void test02(){
//case1
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
Stream<String> stringStream = list.stream()
.map((str) -> str.toUpperCase());
stringStream.forEach(System.out::println);
//结果为“AAA”,"BBB","CCC"
//case2
List<Employee> employees;
employees.stream()
.map(Employee::getName)
.foreach(System.out::println);
}
//flatMap的结果
public void test02(){
String[] words = new String[]{"Hello","World"};
List<String> b = words.stream()
.map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(toList());
b.forEach(System.out::print);//结果为HeloWrd
}
(3)排序
- sorted():自然排序(根据Comparable的compareTo方法)
- 比如String类型已经实现了该方法,默认按照字典序
- sorted(Comparator c):定制排序
public void test03(){
List<Integer> list = Arrays.asList(5,2,2,4,5);
//1. sorted()
list.stream()
.sorted() //comparaTo()
.forEach(System.out::println);//22455
//2. sorted(Comparator c)
list.stream()
.sorted((e1, e2) -> {e1 - e2;}})
.forEach(System.out::println);
}
(4)匹配
- allMatch(Predicate):检查该Predicate是否满足所有元素,如果不是返回False
- anyMatch(Predicate):检查是否至少匹配一个元素
- noneMatch(Predicate):检查是否没有匹配所有元素
public void test04(){
//allMatch
List<Integer> list = Arrays.asList(1, 2, 3 ,4 ,5 ,6);
boolean answer = list.stream()
.allMatch(a -> a > 3);
System.out.println(answer); //false
}
(5)查找与计算
- findFirst:返回流中第一个元素,返回类型为Optional对象
- findAny:返回当前流中的任意元素
- 注:注意parallelStream()和steam()的区别
public void test05(){
//findFirst和findAny的区别
List<String> lst1 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
List<String> lst2 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
Optional<String> findFirst = lst1.stream().filter(s -> s.startsWith("D")).findFirst();
Optional<String> findParallelFirst = lst1.parallelStream().filter(s -> s.startsWith("D")).findFirst();
Optional<String> findAny = lst2.stream().filter(s -> s.startsWith("J")).findAny();
Optional<String> findParallelAny = lst2.parallelStream().filter(s -> s.startsWith("J")).findAny();
System.out.println(findFirst.get()); //总是打印出David
System.out.println(findParallelFirst.get()); //总是打印出David
System.out.println(findAny.get()); //总是打印出David
System.out.println(findParallelAny.get()); //会随机地打印出Jack/Jill/Julia
}
(6)统计
- count:返回流中元素的总个数,返回流中的数量,返回Long
- max() / max(Comparator c):返回流中最大值,不传Comparator,默认compareTo()
- min() / min(Comparator c):返回流中最小值,不传Comparator,默认compareTo()
public void test06(){
List<Integer> list = Arrays.asList(3, 2, 1, 6, 5, 4);
int max = list.stream().max((a, b) -> a > b ? 1 : -1).get(); //max=6
int max = list.stream().min((a, b) -> a > b ? 1 : -1).get(); //min=6
}
(7)归约
- reduce(T identity, BinaryOperator) / reduce(BinaryOperator) 可以将流中的数据反复结合起来,得到一个值
public void test07(){
//reduce求对Integer和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Integer sum = list.stream()
.reduce(0, (x, y) -> x + y);
System.out.println(sum); //1+2+...+8+9=45
//reduce求对某个属性求和,返回Optional对象,因为Salary对象可能为null
Optional<Double> op = employees.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
}
(8)收集:主要配Collectors工具类使用,Collectors工具类中提供了很多常用的静态方法,用于方便地创建常用收集器实例
- collect(Collector c):collect 将流转换成其他形式;接收一个 Collector 接口的实现,用于给流中元素做汇总的方法
(9)Collections工具类
- 1. 创建容器
- Collectors.toList()
- Collectors.toSet()
- Collectors.toMap()
- Collectors.toCurrentMap()
- 定制特定的容器:例如创建LinkedHashSet:Collectors.toCollection(LinkedHashSet::new)
public void test09_1(){
//放入List
List<String> list = emps.stream()
.map(Employee::getName)
.collect(Collectors.toList());
//放入Set
Set<String> set = emps.stream()
.map(Employee::getName)
.collect(Collectors.toSet());
//放入LinkedHashSet
LinkedHashSet<String> linkedHashSet = emps.stream()
.map(Employee::getName)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
- 2. 统计相关
- Collectors.counting():返回总数 Long
- Collectors.averagingInt / Double / Long():返回平均值 Int / Double / Long
- Collectors.summingInt / Double / Long():返回总和 Int / Double / Long
- Collectors.maxBy / minBy(Comparator):排序后取最大 / 最小
public void test09_2(){
//总数
Long count = emps.stream()
.collect(Collectors.counting());
//平均值
Double avg = emps.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
//总和
Double sum = emps.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
//最大值/最小值
Optional<Employee> max = emps.stream()
.collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
}
- 3. 分组
- Collectors.groupBy(Function):按照某个规则分组,返回Map<分组规则,List<>>类型
- 多级分组:Collectors.groupBy(Function,Collectors.groupBy(Function)):先按括号里面的规则分组,分组完继续分组
public void test09_3(){
//1. 普通分组,根据Id分组
Map<Integer, List<Employee>> map = emps.stream()
.collect(Collectors.groupingBy(Employee::getId));
//2. 多级分组
Map<Integer, Map<String, List<Employee>>> mapMap = emps.stream()
.collect(Collectors.groupingBy(Employee::getId, Collectors.groupingBy((e) -> {
if (e.getAge() > 35) {
return "开除";
} else {
return "继续加班";
}
})));
}
- 4. 分区:分成两个区,满足条件的true和不满足条件的false
- Collectors.partitioningBy(Function):返回Map<Boolean, List<>>,满足条件的和不满足条件的
public void test09_4(){
//分区,返回两个组
Map<Boolean, List<Employee>> listMap = emps.stream()
.collect(Collectors.partitioningBy((e) -> e.getSalary() > 10000));
}
- 5. SummaryStatistics类
- DoubleSummaryStatistics / Int... / Long... 类:详细用法见用例
public void test09_5(){
DoubleSummaryStatistics dss = emps.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getMax());
System.out.println(dss.getMin());
System.out.println(dss.getSum());
System.out.println(dss.getCount());
System.out.println(dss.getAverage());
}
- 6. 连接
- Collectors.joining(Function):用于将元素连接之后返回字符串
public void test09_6(){
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
String str = list.stream()
.map(String::valueOf)
.collect(Collectors.joining("-"));
System.out.println(str); //1-2-3-4-5-6
}
8. 串行流与并行流
(1)串行流与并行流
- stream():串行流
- parallelStream():并行流,底层运用ForkJoinPool线程池实现
(2)如何相互转换
- 串 —> 并:parallel()方法,parallelStream()等价于 stream().parallel()
- 并 —> 串:sequential()方法,stream()等价于 parallelStream().sequential()
(3)forEach() 和 forEachOrdered()
- 对于 stream() 流而言
- stream().forEach() 和 stream.forEachOrdered() 没有区别,因为都是串行流,都是严格按照集合的顺序执行的
- 对于 parallelStream() 流而言
- forEach()不保证先后顺序,因为是并发执行
- forEachOrdered() 保证先后顺序,其内部采用多线程同步的方式运行,底层采用Fork/Join框架实现
- 效率比较
- 【for循环】,【stream().forEach()】,【parallelStream.forEachOrdered()】三者效率没有太大差异
- 但【parallelStream(). forEachOrdered()】由于是多线程,与for、stream两种单线程的方式相比,优势在于很好的利用了CPU多核的资源
- 【parallelStream().forEach()】显然效率最高,因为是并行的,但是缺点是不保证前后顺序,需要考虑线程安全问题
9. Optional类
(1)概念
Optional 类 (java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在用 Optional 可以更好的表达这个概念;并且可以避免空指针异常