先了解下Lambda的基础语法:
左侧
:lambda表达式的参数列表
右侧
:lambda表达式中所需要执行的功能,即lambda函数体
lambda表达式语法格式:
1、无参数,无返回值的用法 :() -> System.out.println(“hello lambda”);
2、有一个参数,无返回值的用法: (x) -> System.out.println(x); 或者 x -> System.out.println(x); 一个参数,可以省略参数的小括号
3、有两个参数,有返回值的:(x, y) -> x + y
方法的引用的语法,主要有三类
1.指向静态方法的方法引用,例如Integer的parseInt方法 ,可以写成Integer::parseInt
类::静态方法名
2.指向任意类型实例方法的方法引用,例如String的length方法,写成String::length;
类::实例方法名(也就是非静态方法)
3.指向现有对象的实例方法的方法引用
对象::实例方法名
4、构造器的引用:对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用
ClassName::new
举例子:
/*************** 方法的引用 ****************/
// 类::静态方法名(compare是静态方法)
Comparator<Integer> cam1 = (x, y) -> Integer.compare(x,y);
System.out.println(cam1.compare(3, 2));
Comparator<Integer> cam = Integer::compare;
System.out.println(cam.compare(3, 2));
// 类::实例方法名。(equals是非静态方法)
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("a", "b"));
BiPredicate<String, String> bp1 = String::equals;
System.out.println(bp1.test("a", "b"));
// 对象::实例方法名
Consumer<String> con1 = x -> System.out.println(x);
con1.accept("abc");
Consumer<String> con = System.out::println;
con.accept("abc");
Emp emp = new Emp("上海", "xiaoMIng", 18);
Supplier<String> supper1 = () -> emp.getAddress();
System.out.println(supper1.get());
Supplier<String> supper = emp::getAddress;
System.out.println(supper.get());
/*************** 构造器的引用 ****************/
// 无参构造函数,创建实例
Supplier<Emp> supper2 = () -> new Emp();
Supplier<Emp> supper3 = Emp::new;
Emp emp1 = supper3.get();
emp1.setAddress("上海");
// 一个参数
Function<String, Emp> fun = address -> new Emp(address);
Function<String, Emp> fun1 = Emp::new;
System.out.println(fun1.apply("beijing"));
// 两个参数
BiFunction<String, Integer, Emp> bFun = (name, age) -> new Emp(name, age);
BiFunction<String, Integer, Emp> bFun1 = Emp::new;
System.out.println(bFun1.apply("xiaohong", 18));
至于是选择Consumer,Supplier,Function,BiFunction中的哪一个需要根据你的参数列表,是否有无返回值,返回值类型来决定。
在java8中,lambda表达式所用的接口,必须是函数式接口。
那么怎么定义一个函数式接口。函数式接口定义:接口中只有一个抽象方法的接口,称为函数式接口;
可以使用@FunctionalInterface注解修饰,对该接口做检查;如果接口里,有多个抽象方法,使用该注解,会有语法错误。
1、Consumer<T> 消费型接口:void accept(T t);
2、Supplier<T> 供给型接口。T get();
3.Function<T,R> 函数型接口,R apply(T t);
4.Predicate<T> 断言型接口,或者判断型的接口:boolean test(T t);
JDK 1.8之前已有的函数式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
最后我们列举下java8中 java.util.function包下,内置所有的接口简介和表达的意思
1 BiConsumer<T,U>
代表了一个接受两个输入参数的操作,并且不返回任何结果:void accept(T t, U u);
2 BiFunction<T,U,R>
代表了一个接受两个输入参数的方法,并且返回一个结果
3 BinaryOperator<T>
代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
4 BiPredicate<T,U>
代表了一个两个参数的boolean值方法
5 BooleanSupplier
代表了boolean值结果的提供方
6 Consumer<T>
代表了接受一个输入参数并且无返回的操作
7 DoubleBinaryOperator
代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。
8 DoubleConsumer
代表一个接受double值参数的操作,并且不返回结果。
9 DoubleFunction<R>
代表接受一个double值参数的方法,并且返回结果
10 DoublePredicate
代表一个拥有double值参数的boolean值方法
11 DoubleSupplier
代表一个double值结构的提供方
12 DoubleToIntFunction
接受一个double类型输入,返回一个int类型结果。
13 DoubleToLongFunction
接受一个double类型输入,返回一个long类型结果
14 DoubleUnaryOperator
接受一个参数同为类型double,返回值类型也为double 。
15 Function<T,R>
接受一个输入参数,返回一个结果。
16 IntBinaryOperator
接受两个参数同为类型int,返回值类型也为int 。
17 IntConsumer
接受一个int类型的输入参数,无返回值 。
18 IntFunction<R>
接受一个int类型输入参数,返回一个结果 。
19 IntPredicate
接受一个int输入参数,返回一个布尔值的结果。
20 IntSupplier
无参数,返回一个int类型结果。
21 IntToDoubleFunction
接受一个int类型输入,返回一个double类型结果 。
22 IntToLongFunction
接受一个int类型输入,返回一个long类型结果。
23 IntUnaryOperator
接受一个参数同为类型int,返回值类型也为int 。
24 LongBinaryOperator
接受两个参数同为类型long,返回值类型也为long。
25 LongConsumer
接受一个long类型的输入参数,无返回值。
26 LongFunction<R>
接受一个long类型输入参数,返回一个结果。
27 LongPredicate
R接受一个long输入参数,返回一个布尔值类型结果。
28 LongSupplier
无参数,返回一个结果long类型的值。
29 LongToDoubleFunction
接受一个long类型输入,返回一个double类型结果。
30 LongToIntFunction
接受一个long类型输入,返回一个int类型结果。
31 LongUnaryOperator
接受一个参数同为类型long,返回值类型也为long。
32 ObjDoubleConsumer<T>
接受一个object类型和一个double类型的输入参数,无返回值。
33 ObjIntConsumer<T>
接受一个object类型和一个int类型的输入参数,无返回值。
34 ObjLongConsumer<T>
接受一个object类型和一个long类型的输入参数,无返回值。
35 Predicate<T>
接受一个输入参数,返回一个布尔值结果。
36 Supplier<T>
无参数,返回一个结果。
37 ToDoubleBiFunction<T,U>
接受两个输入参数,返回一个double类型结果
38 ToDoubleFunction<T>
接受一个输入参数,返回一个double类型结果
39 ToIntBiFunction<T,U>
接受两个输入参数,返回一个int类型结果。
40 ToIntFunction<T>
接受一个输入参数,返回一个int类型结果。
41 ToLongBiFunction<T,U>
接受两个输入参数,返回一个long类型结果。
42 ToLongFunction<T>
接受一个输入参数,返回一个long类型结果。
43、UnaryOperator<T>
接受一个参数为类型T,返回值类型也为T。
如果更好的使用上面的方法,我们就需要引入stream,基本上实现了Collection的接口的如:Set,List,Map,SortedSet都可以得到stream
stream的方法:
可以分成两种类型,一种返回类型为接口本身的Stream<T>,另外一种是返回其他对象类型的,返回接口类型的,我们称这些方法为
中间操作
,返回其他具体类型的,我们称为
终端操作
中间操作
,调用stream的方法后返回对象本身就叫中间操作比如StringBuilder类的append方法,调用后返回的还是StringBuilder对象。
终端操作
,是指返回最终的结果,我们常用的forEach内部迭代
———————–中间操作———————–
//map操作
Stream<R> map(Function<? super T, ? extends R> mapper);
//filter操作
Stream<T> filter(Predicate<? super T> predicate);
//flatMap操作
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
//去重复
Stream<T> distinct();
//排序,默认顺序排
Stream<T> sorted();
//根据属性排序
Stream<T> sorted(Comparator<? super T> comparator);
//对对象的本身进行操作,比如在遍历的同时,在修改他某个属性的值。这个和forEach有点类似,但是这个是有返回的值的会得到一个新的stream。
Stream<T> peek(Consumer<? super T> action);
//截断--取先maxSize个对象
Stream<T> limit(long maxSize);
//截断--忽略前N个对象
Stream<T> skip(long n);
———————–终端操作———————–
返回Optional类型。
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
Optional<T> findFirst();
Optional<T> findAny();
findFirst和findAny,通过名字,就可以看到,对这个集合的流,做一系列的中间操作后,可以调用findFirst,返回集合的第一个对象,
findAny返回这个集合中,取到的任何一个对象;通过这样的描述,我们也可以知道,在串行的流中,findAny和findFirst返回的,
都是第一个对象;而在并行的流中,findAny返回的是最快处理完的那个线程的数据,所以说,在并行操作中,对数据没有顺序上的要求,那么findAny的效率会比findFirst要快的
void forEach(Consumer<? super T> action); 不按顺序处理,效率比forEachOrdered高
void forEachOrdered(Consumer<? super T> action); 按顺序处理stream
long count();
boolean anyMatch(Predicate<? super T> predicate);anyMatch表示,判断的条件里,任意一个元素成功,返回true
boolean allMatch(Predicate<? super T> predicate);allMatch表示,判断条件里的元素,所有的都是,返回true
boolean noneMatch(Predicate<? super T> predicate);noneMatch跟allMatch相反,判断条件里的元素,所有的都不是,返回true
———————–collect操作———————–
// 转list
List<String> names = list.stream().map(emp -> emp.getName()).collect(Collectors.toList());
// 转set
Set<String> address = list.stream().map(emp -> emp.getName()).collect(Collectors.toSet());
// 转map,需要指定key和value,Function.identity()表示当前的Emp对象本身
Map<String, Emp> map = list.stream().collect(Collectors.toMap(Emp::getName, Function.identity()));
// 计算元素中的个数
Long count = list.stream().collect(Collectors.counting());
// 数据求和 summingInt summingLong,summingDouble
Integer sumAges = list.stream().collect(Collectors.summingInt(Emp::getAge));
// 平均值 averagingInt,averagingDouble,averagingLong
Double aveAges = list.stream().collect(Collectors.averagingInt(Emp::getAge));
// 综合处理的,求最大值,最小值,平均值,求和操作
// summarizingInt,summarizingLong,summarizingDouble
IntSummaryStatistics intSummary = list.stream().collect(Collectors.summarizingInt(Emp::getAge));
System.out.println(intSummary.getAverage());// 19.5
System.out.println(intSummary.getMax());// 22
System.out.println(intSummary.getMin());// 17
System.out.println(intSummary.getSum());// 117
// 连接字符串,当然也可以使用重载的方法,加上一些前缀,后缀和中间分隔符
String strEmp = list.stream().map(emp -> emp.getName()).collect(Collectors.joining());
String strEmp1 = list.stream().map(emp -> emp.getName()).collect(Collectors.joining("-中间的分隔符-"));
String strEmp2 = list.stream().map(emp -> emp.getName()).collect(Collectors.joining("-中间的分隔符-", "前缀*", "&后缀"));
最大值
Optional<Integer> maxAge = list.stream().map(emp -> emp.getAge()).collect(Collectors.maxBy(Comparator.comparing(Function.identity())));
// 最小值
Optional<Integer> minAge = list.stream().map(emp -> emp.getAge()).collect(Collectors.minBy(Comparator.comparing(Function.identity())));
// 归约操作
list.stream().map(emp -> emp.getAge()).collect(Collectors.reducing((x, y) -> x + y));
list.stream().map(emp -> emp.getAge()).collect(Collectors.reducing(0, (x, y) -> x + y));
// 分操作 groupingBy 根据地址,把原list进行分组
Map<String, List<Emp>> mapGroup = list.stream().collect(Collectors.groupingBy(Emp::getAddress));
// partitioningBy 分区操作 需要根据类型指定判断分区
Map<Boolean, List<Integer>> partitioningMap = list.stream().map(emp -> emp.getAge()).collect(Collectors.partitioningBy(emp -> emp > 20));