JDK1.8 Lambda & Stream
参考链接:
https://www.cnblogs.com/CarpenterLee/p/6637118.html#4486817
https://www.cnblogs.com/CarpenterLee/p/6675568.html
https://blog.csdn.net/xiliunian/article/details/88343762
https://blog.csdn.net/xiliunian/article/details/88364200
https://blog.csdn.net/xiliunian/article/details/88773718
数据源
下述所有操作皆基于此
List<Engineer> engineerList = Lists.newArrayList();
@Before
public void init(){
engineerList.add(new Engineer().setId(1).setAge(12).setName("张三").setSalary(new BigDecimal("1000")));
engineerList.add(new Engineer().setId(2).setAge(14).setName("李四").setSalary(new BigDecimal("2000")));
engineerList.add(new Engineer().setId(3).setAge(20).setName("王五").setSalary(new BigDecimal("5000")));
engineerList.add(new Engineer().setId(4).setAge(18).setName("赵六").setSalary(new BigDecimal("8000")));
engineerList.add(new Engineer().setId(5).setAge(40).setName("陈七").setSalary(new BigDecimal("20000")));
engineerList.add(new Engineer().setId(6).setAge(20).setName("钱八").setSalary(new BigDecimal("17000")));
}
引言
lambda
何为
lambda
,对于java来说万物皆对象。但是有些时候我们的参数可能只需要一个处理不需要做那么重的操作。故此我们有了lambda
函数式编程。其标准格式为:(参数...) -> {代码块}
左侧小括号内的参数无则留空例如
() -> {1}
;一个则小括号可以直接省略例如req -> {req == null}
;有多个则逗号间隔(req1,req2,req3) -> {}
。右侧大括号内邪恶参数若只有一个则括号可以省略例如
req -> req == null
;如果有返回对象且语句只有一条则可以省略return
关键字例如req -> req.getName()
;如果有多条语句需要用;
间隔有返回值则return
不可省略例如req -> {String name = req.getName();\r\n return name+"-test";}
注意:
lambda虽然为函数但是其内部得局部变量不能与外界得方法的变量同名
lambda的
return
不会导致调用method
的终止其只是终止了当前函数。lambda表达式允许用引用final变量、static变量和局部变量但是只允许修改静态变量,以及修改局部变量的属性而不可改变变量的引用指向。如果我们想使用局部变量需要保证此局部变量在lambda外没有使用。
为什么会有如上这种限制呢?主要是因为局部变量的引用及基本类型的局部变量都保存在栈当中。如若
lambda
能直接修改栈上的变量,lambda
在另一条线程运行(不一定是新线程运行),那么lambda使用后,当前方法继续执行后其访问的该变量并非是原始变量而是副本。故此我们用开发工具写lambda的时候如果直接使用局部变量有时会报错提示需要复制一个变量出来使用,而使用静态变量则不需要因为静态变量是全局的存放在堆当中的可以直接修改值。由于对局部变量的限制,Lambda 表达式在 Java 中又称为闭包或匿名函数。它们可以作为参数传递给方法,并且可以访问其作用域之外的变量。但是它们不能修改定义 Lambda 的方法的局部变量的内容,这些变量必须是隐式最终的。因此可以认为 Lambda 是对值封闭,而不是对变量封闭,因为可以访问局部变量,但不可修改值。
Stream
何为
Stream
,数据流式操作,流一旦开启数据则数据不会再新增和删除,流是无法复用这意味着一个Stream
只能被使用一次。我们可以用不同的方法来对流的数据进行不同的操作如filter(过滤)
,map(提取)
,reduce(汇总)
、find(单个数据)
,match(计算)
,sort(排序)
等.这让我们在一定程度上避免了遍历和if语句的编写,对集合的操作有了更高层次的抽象。Stream
的操作分为Intermediate
(中间)操作、Terminal
(终端)及short-circuiting
操作。上诉所说的filter
、map
皆是Intermediate
。可以有多个中间操作存在,我们可以将多项操作串联起来表达出一个复杂的处理例如先过滤再提取接着汇总然后输出Termianl
。Stream
的操作可以单线程执行,也可以使用并行流parallelStream
。Stream
接口通常以函数式接口作为参数,因此其对lambda
的支持非常好,Stream
配合Lambda
可以写出非常简练的链式代码。<b><font color=Red size=4>注意:</font></b>
- Stream不具有复用性,使用一次下次若再次使用会报错
stream has already been operated upon or closed
- Strem中 如果没有Terminal(终端) 语句Intermediate(中间操作)将不会运行,没有终端去输出,过程将不会运行
<b><font color=Red size=4>构造方法:</font></b>
list.stream();//通过list获得 Stream.of(T t...); // 通过Stream.of 获得与 Arrays.asList() Stream.if(T[]);//通过Stream.of 数组获得 List.parallelStream();//通过List获得并行流 Stream.of(T t...).parallel();//将Stream转为并行流
可以做到将一个
Stream
转为并行的多个stream流,其开辟线程数默认和CPU 核数一致。其默认通过JDK1.7的ForJoinPool来实现的多线程任务。System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","12")
可以做到修改其线程数,不推荐修改。
常量词解释
@FunctionalInterface 函数式接口
FunctionalInterface 首先它得是一个接口,然后就是在这个接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。
注意:虽然它只能有一个抽象方法,但是他允许定义默认方法和静态方法。例如下述的 [Consumer](#noun -consumer) 就是一个典型的函数式接口
<a name="noun -consumer"/>
Consumer(消费者)
`Consumer` 接口就是一个消费型的接口,通过传入参数,然后处理此参数。即可写作 `(T t) -> {}`。其有一个默认的方法`andThen` 会返回一个`Consumer` ,默认实现的是在 当前调用者的`accept`之后再调用传入的`after` 的`accept`方法。
与Consumer
相同的有DoubleConsumer、IntConsumer、LongConsumer
与之类似的有BiConsumer
。BiConsumer
为两个参数也叫(二元消费者)
@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:
@Test
public void toConsumer(){
//实现了Consumer接口
Consumer<Engineer> engineerConsumer = new Consumer<Engineer>() {
@Override
public void accept(Engineer e) {
e.setAge(e.getAge()+100);
}
};
//将集合中的元素的age加上100
testConsumer(engineerList,engineerConsumer);
engineerList.forEach(System.out::println);
System.out.println("==========lambda表达式==========");
engineerList.forEach(t -> t.setAge(t.getAge()+100));
engineerList.forEach(System.out::println);
}
/**
* 将集合中的属性通过 Consumer 进行处理处理
*/
private static <T> void testConsumer(List<T> list,Consumer<T> consumer){
for (T t : list) {
consumer.accept(t);
}
}
结果:
Engineer(id=1, age=112, name=张三, salary=1000)
Engineer(id=2, age=114, name=李四, salary=2000)
Engineer(id=3, age=120, name=王五, salary=5000)
Engineer(id=4, age=118, name=赵六, salary=8000)
Engineer(id=5, age=140, name=陈七, salary=20000)
Engineer(id=6, age=120, name=钱八, salary=17000)
==========lambda表达式==========
Engineer(id=1, age=212, name=张三, salary=1000)
Engineer(id=2, age=214, name=李四, salary=2000)
Engineer(id=3, age=220, name=王五, salary=5000)
Engineer(id=4, age=218, name=赵六, salary=8000)
Engineer(id=5, age=240, name=陈七, salary=20000)
Engineer(id=6, age=220, name=钱八, salary=17000)
例2: Consumer的andThen
@Test
public void toConsumer2() {
//实现了Consumer接口
Consumer<Engineer> engineerConsumer = new Consumer<Engineer>() {
@Override
public void accept(Engineer e) {
e.setName(e.getName() + "-电商组");
}
};
// 所有技工的名字加上(研发部)-电商组
engineerList.forEach(engineerConsumer.andThen(t -> t.setName(t.getName() + "[研发二部]")));
engineerList.forEach(System.out::println);
}
结果:
Engineer(id=1, age=12, name=张三-电商组[研发二部], salary=1000)
Engineer(id=2, age=14, name=李四-电商组[研发二部], salary=2000)
Engineer(id=3, age=20, name=王五-电商组[研发二部], salary=5000)
Engineer(id=4, age=18, name=赵六-电商组[研发二部], salary=8000)
Engineer(id=5, age=40, name=陈七-电商组[研发二部], salary=20000)
Engineer(id=6, age=20, name=钱八-电商组[研发二部], salary=17000)
Supplier(提供者)
supplier 是一个类似于参数构建的接口,仅有一个 `get()`方法会返回一个参数。其可以提供一个参数供其他方法调用。即可写作 `() -> T`;与之相同的接口还有`IntSupplier 、DoubleSupplier 、LongSupplier 、BooleanSupplier`;`orEleseGet`就是一个典型的`Supplier`
@FunctionalInterface
public interface Supplier<T> {
//抽象接口
T get();
}
例1:
Engineer engineer = null;
Supplier<Engineer> supplier = new Supplier<Engineer>() {
@Override
public Engineer get() {
// 返回一个对象
return new Engineer().setName("张三").setSalary(new BigDecimal(new Random().nextInt()));
}
};
engineer = Optional.ofNullable(engineer).orElseGet(supplier);
System.out.println(engineer);
结果:
Engineer(age=null, name=张三, salary=-120016191)
Predicate (谓语;条件判断)
Predicate 接口是一个谓词型接口,其实,这个就是一个类似于 bool 类型的条件判断的接口。提供接口`test`传入一个参数返回一个`boolean`值。用`lambda`表达式可以写作`(T t) -> return true|false`.其内部自身还维护了条件运算符的一些功能及一个`hashCode`判断的静态方法。`Stream`中的`filter` 为一个`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);
}
//参数等比
//不同于以上这个是一个静态方法用以对比对象hashCode
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
例1:
Predicate<Engineer> predicate = new Predicate<Engineer>() {
@Override
public boolean test(Engineer engineer) {
return engineer.getName() != null && engineer.getName().equals("张三");
}
};
engineerList.stream().filter(predicate).forEach(System.out::println);
结果:
Engineer(id=1, age=12, name=张三, salary=1000)
例2:条件与(and)
age > 14且sallary > 5000
Predicate<Engineer> predicate = new Predicate<Engineer>() {
@Override
public boolean test(Engineer engineer) {
return engineer.getName() != null && engineer.getAge() > 14;
}
};
//条件与
System.out.println("========= 条件与 =========");
Predicate<Engineer> andPredicate = predicate.and((t) ->
new BigDecimal("5000").compareTo(t.getSalary()) < 0);
engineerList.stream().filter(andPredicate).forEach(System.out::println);
结果:
========= 条件与 =========
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=6, age=20, name=钱八, salary=17000)
例3:条件或(or)
age > 14 或name 包含 陈
Predicate<Engineer> predicate = new Predicate<Engineer>() {
@Override
public boolean test(Engineer engineer) {
return engineer.getName() != null && engineer.getAge() > 14;
}
};
//条件或
System.out.println("========= 条件或 =========");
Predicate<Engineer> orPredicate = predicate.or(t -> t.getName().contains("陈"));
engineerList.stream().filter(orPredicate).forEach(System.out::println);
结果:
========= 条件或 =========
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=6, age=20, name=钱八, salary=17000)
例4:条件否定(negate)
!(age > 14)
Predicate<Engineer> predicate = new Predicate<Engineer>() {
@Override
public boolean test(Engineer engineer) {
return engineer.getName() != null && engineer.getAge() > 14;
}
};
//条件否定
System.out.println("========= 条件否定 =========");
engineerList.stream().filter(predicate.negate()).forEach(System.out::println);
结果:
========= 条件否定 =========
Engineer(id=1, age=12, name=张三, salary=1000)
Engineer(id=2, age=14, name=李四, salary=2000)
例5:对象比对(Hash)
equals
System.out.println("========= 传参对比 Hash 比对=========");
Engineer targetCompareEngineer =
new Engineer().setId(6).setAge(20).setName("钱八").setSalary(new BigDecimal("17000"));
engineerList.stream().filter(Predicate.isEqual(targetCompareEngineer))
.forEach(System.out::println);
结果:
========= 传参对比 Hash 比对=========
Engineer(id=6, age=20, name=钱八, salary=17000)
Function(中转器)-UnaryOperator-BinaryOperator
Function<T,R>
接口为一个功能型接口。提供接口apply
传入一个参数R
转换成参数T
。顾名思义其就是一个函数。用lambda
表达式可以写作r -> t
。与Function
相同的有IntFunction<R>
(将Integer
转为R
)、IntToDoubleFunction
(提供applyAsDouble
将Integer
转为Double
)、LongFunction<R>
、UnaryOperator<T>(出入参一致)
、BiFunction<T,T,T>
等
<font color=Blue>Function</font>
@FunctionalInterface
public interface Function<T, R> {
//抽象的校验方法
R apply(T t);
//前置处理 接受一个方法返回一个参数并把返回参数带入到apply参与处理
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
//后置处理 接受一个参数返回一个参数将当前的apply的运算结果带入其中参与处理
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
//静态方法返回一个输入和输出一致的Function
static <T> Function<T, T> identity() {
return t -> t;
}
}
<font color=Blue>***UnaryOperator<T>***</font>
继承于Function<T,R>不过它的出入参数是同样一个参数。也就是说,传入泛型T类型的参数,调用apply后,返回也T类型的参数;这个接口定义了一个静态方法,返回泛型对象的本身;
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
/**
* Returns a unary operator that always returns its input argument.
*
* @param <T> the type of the input and output of the operator
* @return a unary operator that always returns its input argument
*/
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
<a name="BinaryOperator"></a>
<font color=Blue>***BinaryOperator<T>***</font>
继承于BiFunction<T, U, R>
不过BinaryOperator<T>
的输入参数是同样一个参数,也就是说,传入泛型T类型的参数,调用apply后,返回也T类型的参数。不过其内置了两个静态方法minBy
和maxBy
。
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
/**
* 通过比较器Comparator来比较两个元素中较小的一个作为返回值返回。
* @param <T> 比较器的输入参数的类型
* @param comparator 用来比较两个值的Comparator
* @return 通过比较器Comparator来比较两个元素中较小的一个作为返回值返回。
* @throws 如果参数为NULL,就会抛出NullPointerException异常
*/
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
/**
* 通过比较器Comparator来比较两个元素中较大的一个作为返回值返回。
* @param <T> 比较器的输入参数的类型
* @param comparator 用来比较两个值的Comparator
* @return 通过比较器Comparator来比较两个元素中较小的一个作为返回值返回。
* @throws 如果参数为NULL,就会抛出NullPointerException异常
*/
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
例1:
Function<Engineer, String> engineerStringFunction = new Function<Engineer, String>() {
@Override
public String apply(Engineer e) {
return e.getName();
}
};
Stream<String> stringStream = engineerList.stream().map(engineerStringFunction);
stringStream.forEach(System.out::print);
System.out.println();
// 利用lambda表达式去写
System.out.println("===========利用lambda表达式去写==============");
engineerList.stream().map(Engineer::getName).forEach(System.out::print);
结果:
张三李四王五赵六陈七钱八
===========利用lambda表达式去写==============
张三李四王五赵六陈七钱八
例2:前置处理(compose)
- e.setName(e.getName() + "-compose")
- return name
Function<Engineer, String> engineerStringFunction = new Function<Engineer, String>() {
@Override
public String apply(Engineer e) {
return e.getName();
}
};
//处理前置组成 compose 前置处理
System.out.println("========= 前置处理 =========");
Stream<String> stringStream1 = engineerList.stream().map(
engineerStringFunction.compose((e) -> e.setName(e.getName() + "-compose")));
stringStream1.forEach(System.out::println);
结果:
========= 前置处理 =========
张三-compose
李四-compose
王五-compose
赵六-compose
陈七-compose
钱八-compose
例3:后置处理(andThen)
- return name
- name + "-andThen"
Function<Engineer, String> engineerStringFunction = new Function<Engineer, String>() {
@Override
public String apply(Engineer e) {
return e.getName();
}
};
//处理后置组成
System.out.println("========= 后置处理 =========");
Stream<String> stringStream2 = engineerList.stream().map(
engineerStringFunction.andThen((e) -> e + "-andThen"));
stringStream2.forEach(System.out::println);
结果:
========= 后置处理 =========
张三-andThen
李四-andThen
王五-andThen
赵六-andThen
陈七-andThen
钱八-andThen
例4:出入参一致(identity)
Function<Engineer, String> engineerStringFunction = new Function<Engineer, String>() {
@Override
public String apply(Engineer e) {
return e.getName();
}
};
//出入参一致
System.out.println("========= 出入参一致 =========");
Map<BigDecimal, Engineer> collect = engineerList.stream().collect(Collectors.toMap(Engineer::getSalary, Function.identity()));
Map<BigDecimal, String> collect1 = engineerList.stream().collect(Collectors.toMap(Engineer::getSalary, engineerStringFunction));
collect.forEach((k, v) -> System.out.println("使用 identity 时 =>[" + k + ":" + v + "]"));
collect1.forEach((k, v) -> System.out.println("使用 engineerStringFunction 时 =>[" + k + ":" + v + "]"));
结果:
========= 出入参一致 =========
使用 identity 时 =>[17000:Engineer(id=6, age=20, name=钱八, salary=17000)]
使用 identity 时 =>[2000:Engineer(id=2, age=14, name=李四, salary=2000)]
使用 identity 时 =>[8000:Engineer(id=4, age=18, name=赵六, salary=8000)]
使用 identity 时 =>[1000:Engineer(id=1, age=12, name=张三, salary=1000)]
使用 identity 时 =>[20000:Engineer(id=5, age=40, name=陈七, salary=20000)]
使用 identity 时 =>[5000:Engineer(id=3, age=20, name=王五, salary=5000)]
使用 engineerStringFunction 时 =>[17000:钱八]
使用 engineerStringFunction 时 =>[2000:李四]
使用 engineerStringFunction 时 =>[8000:赵六]
使用 engineerStringFunction 时 =>[1000:张三]
使用 engineerStringFunction 时 =>[20000:陈七]
使用 engineerStringFunction 时 =>[5000:王五]
例6: BinaryOperator和其minBy
BinaryOperator<String> stringBinaryOperator = (t1,t2) -> t1+t2;
System.out.println(stringBinaryOperator.apply("a", "b"));
/** minBy */
BinaryOperator<Engineer> engineerBinaryOperator = BinaryOperator.minBy((t1, t2) -> (t1.getSalary().compareTo(t2.getSalary())));
Engineer apply = engineerBinaryOperator.apply(
new Engineer().setName("张三").setSalary(new BigDecimal("5.0")),
new Engineer().setName("李四").setSalary(new BigDecimal("23.0")));
System.out.println(apply);
Collector接口
Collector接口用于Stream的collect方法的参数;其有三个泛型分别是:
**T:** 单个元素的类型
**A:**累计用的容器类型
**R:**输出的容器的类型
public interface Stream<T>{
<R, A> R collect(Collector<? super T, A, R> collector);
}
其包含五个参数,正因为这五个参数所以我们呢有了很多可以操作的工具方法例如:toStet
、toList
等
public static<T, A, R> Collector<T, A, R> of(
// supplier参数用于生成结果容器,容器类型为A
Supplier<A> supplier,
//accumulator用于消费元素,这里的T就是元素,它会将流中的元素一个一个与结果容器A发生操作
BiConsumer<A, T> accumulator,
//combiner用于两个两个合并并行执行的线程的执行结果,将其合并为一个最终结果A
BinaryOperator<A> combiner,
//finisher用于将结果转换将结果R 转为 结果A
Function<A, R> finisher,
//characteristics 表示当前Collector的特征值
Characteristics... characteristics
){
...
}
特征码Characteristics
包括:CONCURRENT
、UNORDERED
、IDENTITY_FINISH
.