lambda 随笔
@
- JDK1.8 Lambda & Stream
- 数据源
- 引言
- 常量词解释
- Stream的一些操作
- Optional
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
可以写出非常简练的链式代码。注意:
- Stream不具有复用性,使用一次下次若再次使用会报错
stream has already been operated upon or closed
- Strem中 如果没有Terminal(终端) 语句Intermediate(中间操作)将不会运行,没有终端去输出,过程将不会运行
构造方法:
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) 就是一个典型的函数式接口
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>
等
Function
@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;
}
}
UnaryOperator
继承于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;
}
}
BinaryOperator
继承于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
.
- CONCURRENT: 结果只有一个,如果是并行流其将会触发combiner去进行结果合并;此特性意味着多条线程可以同时操作一个结果容器,因而结果容器必须为线程安全的。
- UNORDERED:流中的元素无序,没屌用的特征码🐂
- IDENTITY_FINISH: 表示中间操作所得类型与最终结果类型一致无须调用
finisher
进行转换。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jZ2AImKA-1615448693776)(C:\Users\雁鹏\AppData\Roaming\Typora\typora-user-images\image-20201030144339459.png)]
例1: toList() 源码解读
static final Set<Collector.Characteristics> CH_ID
= Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>(
(Supplier<List<T>>) ArrayList::new,
List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID
);
}
其首先创建了一个ArrayList集合(supplier
),然后把每个元素通过List.add(T)给循环加入(accumulator
),通过(left, right) -> { left.addAll(right); return left; }
去归并并行流(combiner
),CH_ID为特征码IDENTITY_FINISH
。
CollectorImpl(Supplier<A> supplier,
BiConsumer<A, T> accumulator,
BinaryOperator<A> combiner,
Set<Characteristics> characteristics) {
this(supplier, accumulator, combiner, castingIdentity(), characteristics);
}
private static <I, R> Function<I, R> castingIdentity() {
return i -> (R) i;
}
castingIdentity
为一个直接类型强转的方法i -> (R)i
;
例2: 自定义Collector
public class TestSetCollector implements Collector<Engineer, Set<Engineer>, Set<Engineer>> {
private static AtomicInteger pointer = new AtomicInteger(0);
//定义一个HashSet来归集元素
@Override
public Supplier<Set<Engineer>> supplier() {
System.out.println(pointer.getAndIncrement() + "==>[supplier]调用");
return new Supplier<Set<Engineer>>() {
@Override
public Set<Engineer> get() {
return Sets.newHashSet();
}
};
}
@Override
public BiConsumer<Set<Engineer>, Engineer> accumulator() {
System.out.println(pointer.getAndIncrement() + "==>[accumulator]调用");
return new BiConsumer<Set<Engineer>, Engineer>() {
@Override
public void accept(Set<Engineer> s, Engineer str) {
s.add(str);
}
};
}
@Override
public BinaryOperator<Set<Engineer>> combiner() {
System.out.println(pointer.getAndIncrement() + "==>[combiner]调用");
return new BinaryOperator<Set<Engineer>>() {
@Override
public Set<Engineer> apply(Set<Engineer> s1, Set<Engineer> s2) {
s1.addAll(s2);
return s1;
}
};
}
@Override
public Function<Set<Engineer>, Set<Engineer>> finisher() {
System.out.println(pointer.getAndIncrement() + "==>[finisher]调用");
return new Function<Set<Engineer>, Set<Engineer>>() {
@Override
public Set<Engineer> apply(Set<Engineer> s1) {
s1.add(new Engineer().setName("你被转换过了的"));
return s1;
}
};
}
@Override
public Set<Characteristics> characteristics() {
System.out.println(pointer.getAndIncrement() + "==>[characteristics]调用");
return Sets.newHashSet(Characteristics.IDENTITY_FINISH,
Characteristics.CONCURRENT,
Characteristics.UNORDERED);
}
}
结果:
0==>[supplier]调用
1==>[accumulator]调用
2==>[combiner]调用
3==>[characteristics]调用
4==>[characteristics]调用
Engineer(id=8, age=24, name=李十, salary=5000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=7, age=30, name=孙九, salary=8000)
...
执行语句
@Test
public void testCollect() {
engineerList.stream().collect(new TestSetCollector()).forEach(System.out::println);
}
从打印结果看finisher()
函数并没有被执行,原因在于我们在TestSetCollector.characteristics
定义的特征码IDENTITY_FINISH
.其在java.util.stream.ReferencePipeline#collect
中有对其进行判断当特征码包含IDENTITY_FINISH
的话则不需要进行finisher
public final <R, A> R collect(Collector<? super P_OUT, A, R> collector) {
A container;
if (isParallel()
&& (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
&& (!isOrdered()||collector.characteristics().contains(Collector.Characteristics.UNORDERED))) {
//supplier的执行
container = collector.supplier().get();
BiConsumer<A, ? super P_OUT> accumulator = collector.accumulator();
//accumulator的执行
forEach(u -> accumulator.accept(container, u));
}else {
container = evaluate(ReduceOps.makeRef(collector));
}
//判断特征码是否包含IDENTITY_FINISH包含则直接强转,否则调用 finisher.apply
return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)
? (R) container
: collector.finisher().apply(container);
}
Stream的一些操作
Intermediate (中间操作)
操作 | 含义 |
---|---|
map (mapToInt, flatMap 等) | 对象属性刷选 |
filter | 条件过滤 |
distinct | 去重 |
sorted;Comparator;sort | 排序 |
peek | 循环处理元素 |
limit | 获取前n个元素 |
skip | 删除前n个元素 |
parallel | stream转为并行流 |
sequential | stream转为串行流 |
unordered | 将流无序化, 用于对顺序无要求的Stream 例如并行流其可以提升性能 |
Map;mapToInt;mapToDouble;..
其可以对Stream进行数据的刷选映射到另一个Strem对象上去(新对象为新创建),新的Stream对象的类型与提取值得类型有关。其内置了Int、Double和Long三个Map。
用法
//传入一个 function 函数 Stream stream = stream.map(Function<? super T, ? extends R> mapper); //通过 lambda去提取值,如果对提取值有更多处理可用此方式 Stream stream1 = stream.mapToInt(engineers -> engineers.getAge()); //通过 Function 函数去获取参数值。 Stream stream2 = stream.mapToInt(Engineer::getAge);
例1:
Stream<Engineer> stream = engineers.parallelStream();
IntStream intStream = stream.mapToInt(engineers -> engineers.getAge());
intStream.forEach(e -> System.out.println(e));
结果:
the age is =>20
the age is =>18
the age is =>15
the age is =>14
filter 指定条件过滤
其可以对Stream中的对象进行条件过滤,输出过滤后的结果
用法
// 传入一个 Predicate Stream<T> stream = = stream.filter(Predicate<? super T> predicate); Stream<T> stream = = stream.filter(Predicate<T>); //传入一个Predicate 函数 其会要求返回一个boolean值 Stream<T> stream = = stream.filter(engineer -> engineer.getAge() >= 18);//直接使用lambda进行
例1:
// ------- 获取age 大于等于18的 对象
Stream<Engineer> stream = engineers.stream(); //获得stream流
//过滤enginner取出age大于等于18的转为stream流
Stream<Engineer> engineerStream = stream.filter(engineer -> engineer.getAge() >= 18);
//循环输出
engineerStream.forEach(System.out::println);
结果:
Engineer{name='陈七', age=18, salary=2244}
Engineer{name='段誉', age=20, salary=7386}
Engineer{name='虚竹', age=21, salary=5648}
例2:
engineerList.stream().filter(e -> e.getAge() > 15).map(Engineer::getName).forEach(System.out::println);
distinct 根据hashCode去重
数据去重,与mysql的DISTINCT() 函数等意。其会根据hashCode 进行去重和Set类似
用法:
Stream<T> distinctStream = Stream.distinct();
例1:
//将integer去重
Stream<Integer> intStream = Stream.of(1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 8, 9, 10).parallel();
Stream<Integer> distinct = intStream.distinct();
distinct.forEach(t -> System.out.println("result =>"+t));
运行结果:
result =>7
result =>6
result =>2
result =>3
result =>1
result =>8
result =>4
result =>9
result =>10
result =>5
例2: 去重的几种方式
//根据hashCode去重,可以直接使用 distinct
System.out.println("===========使用distinct===========");
engineerList.stream().distinct().collect(Collectors.toList()).forEach(System.out::println);
//根据hashCode去重,使用HashSet去重,
//区别于 distinct;distinct使用后stream任然处于 Intermediate(中间操作)的状态而HashSet处于Terminal(终端)状态
System.out.println("===========使用HashSet===========");
engineerList.stream().collect(Collectors.toSet()).forEach(System.out::println);
//根据 TreeSet 能传入 Comparator 来形成指定属性(name)的去重来把集合中的元素放入到TreeSet集合,然后把TreeSet集合转为List
//collectingAndThen解释: Collectors.collectingAndThen 将 集合通过 Collector 操作后再通过后续的Function 得到结果
//toCollection解释: 通过 Supplier构建一个参数将集合中的元素放入其中
System.out.println("===========使用Comparator来指定去重===========");
List<Engineer> collect = engineerList.stream().collect(
Collectors.collectingAndThen(
Collectors.toCollection(() ->
Sets.newTreeSet(Comparator.comparing(Engineer::getName))), Lists::newArrayList));
collect.forEach(System.out::println);
//使用filter来去重,使用外部属性来根据contain去重
System.out.println("===========使用 filter 来指定去重===========");
List<BigDecimal> containSalaryList = Lists.newArrayList();
engineerList.stream().filter(t -> {
//判断集合是否有 salary 有则过滤 否则添加
if (containSalaryList.contains(t.getSalary())) {
return false;
}
containSalaryList.add(t.getSalary());
return true;
}).forEach(System.out::println);
运行结果:
===========使用distinct===========
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=7000)
Engineer(id=7, age=30, name=孙九, salary=8000)
Engineer(id=8, age=24, name=李十, salary=5000)
===========使用HashSet===========
Engineer(id=8, age=24, name=李十, salary=5000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=7, age=30, name=孙九, salary=8000)
Engineer(id=6, age=20, name=钱八, salary=7000)
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=1, age=12, name=张三, salary=1000)
===========使用Comparator来指定去重===========
Engineer(id=7, age=30, name=孙九, salary=8000)
Engineer(id=1, age=12, name=张三, salary=1000)
Engineer(id=8, age=24, name=李十, salary=5000)
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=6, age=20, name=钱八, salary=7000)
Engineer(id=5, age=40, name=陈七, salary=20000)
===========使用 filter 来指定去重===========
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=7000)
sorted;Comparator;sort;
数据排序,其会根据 java.util.Comparator
进行排序,也可以自行重写排序规则。Comparator 接口为一个函数式接口,可以使用lambda表达式。sort
和 sorted
的区别在于 sort
用于集合 list.sort(...)
而sorted
用于流stream
.
用法:
//要求Comparator 返回一个Integer list.sort(Comparator<T>); Stream<Engineer> sorted = stream.sorted(Comparator<? super T> comparator); objects.sort(Comparator.comparing(T::getXX()));//根据对象 T 的xx 属性来排序(升序) objects.sort(Comparator.comparing(T::getXX()).reversed());//根据对象 T 的xx 属性来排序然后取反
注意: stored因涉及多参数的比较,故不要使用并行流 ParallelStream来进行操作。
Comparator 可以使用 reversed()来取反。
Comparator
函数源码如下:@FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); //反转 default Comparator<T> reversed() { return Collections.reverseOrder(this); } }
可以简写lambda表达式为
(t1,t2) -> return Integer
. 负数为升序 正数为降序
例1: sort
ArrayList<Engineer> objects = Lists.newArrayList();
objects.add(new Engineer().setName("段誉").setAge(20).setSalary(4581L));
objects.add(new Engineer().setName("王五").setAge(14).setSalary(4481L));
objects.add(new Engineer().setName("陈六").setAge(30).setSalary(5581L));
objects.add(new Engineer().setName("虚竹").setAge(18).setSalary(9581L));
objects.sort(Comparator.comparing(Engineer::getAge));
System.out.println("age升序:"+objects);
objects.sort(Comparator.comparing(Engineer::getAge).reversed());
System.out.println("age降序:"+objects);
结果:
age升序:[
AutoGenerate.Engineer(name=王五, age=14, salary=4481),
AutoGenerate.Engineer(name=虚竹, age=18, salary=9581),
AutoGenerate.Engineer(name=段誉, age=20, salary=4581),
AutoGenerate.Engineer(name=陈六, age=30, salary=5581)
]
age降序:[
AutoGenerate.Engineer(name=陈六, age=30, salary=5581),
AutoGenerate.Engineer(name=段誉, age=20, salary=4581),
AutoGenerate.Engineer(name=虚竹, age=18, salary=9581),
AutoGenerate.Engineer(name=王五, age=14, salary=4481)
]
例2:storted
根据age 升序
Comparator<Engineer> comparator = new Comparator<Engineer>() {
@Override
public int compare(Engineer o1, Engineer o2) {
return o1.getSalary().compareTo(o2.getSalary());
}
};
engineerList.stream().sorted(comparator).forEach(System.out::println);
System.out.println("=========反转======");
engineerList.stream().sorted(comparator.reversed()).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=6, age=20, name=钱八, salary=17000)
Engineer(id=5, age=40, name=陈七, salary=20000)
=========反转======
Engineer(id=5, age=40, name=陈七, salary=20000)
Engineer(id=6, age=20, name=钱八, salary=17000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=1, age=12, name=张三, salary=1000)
例3: lambda表达式 sorted
engineerList.stream().sorted((t1, t2) -> t1.getAge() - t2.getAge()).forEach(System.out::println);
//engineerList.stream().sorted(Comparator.comparingInt(Engineer::getAge)).forEach(System.out::println);//与上等价
结果:
Engineer(id=1, age=12, name=张三, salary=1000)
Engineer(id=2, age=14, name=李四, salary=2000)
Engineer(id=4, age=18, name=赵六, salary=8000)
Engineer(id=3, age=20, name=王五, salary=5000)
Engineer(id=6, age=20, name=钱八, salary=17000)
Engineer(id=5, age=40, name=陈七, salary=20000)
例4: 根据指定字符排序
RuleBasedCollator
根据指定的<?<?
去排序字符。其会子匹配头表示。
Comparator<UserDto> comparator = new Comparator<UserDto>() {
@Override
public int compare(UserDto o1, UserDto o2) {
String myrule = "<陈<姜<罗<徐";
RuleBasedCollator myrulecollato = null;
try {
myrulecollato = new RuleBasedCollator(myrule);
} catch (ParseException e) {
e.printStackTrace();
}
return myrulecollato.compare(o1.getAuthor(), o2.getAuthor());
}
};
e.g.:
ArrayList<UserDto> userDtoList = new ArrayList<>();
userDtoList.add(new UserDto("让子弹飞","姜文",42));
userDtoList.add(new UserDto("一位陌生女人的来信","徐静蕾",35));//x
userDtoList.add(new UserDto("雾都孤儿","罗曼·波兰斯基",52));//l
userDtoList.add(new UserDto("芙蓉镇","姜文",36));//j
userDtoList.add(new UserDto("我和我的祖国","陈凯歌",46));
userDtoList.add(new UserDto("霸王别姬","陈凯歌",34));
userDtoList.add(new UserDto("金陵十三钗","张艺谋",34));
userDtoList.sort(comparator);
userDtoList.forEach(System.out::println);
结果:
UserDto{names='我和我的祖国', author='陈凯歌', age=46}
UserDto{names='霸王别姬', author='陈凯歌', age=34}
UserDto{names='让子弹飞', author='姜文', age=42}
UserDto{names='芙蓉镇', author='姜文', age=36}
UserDto{names='雾都孤儿', author='罗曼·波兰斯基', age=52}
UserDto{names='一位陌生女人的来信', author='徐静蕾', age=35}
UserDto{names='金陵十三钗', author='张艺谋', age=34}
peek 循环处理
循环处理元素与ForEach类似,区别是ForEach操作是属于Terminal
操作,元素将会被消费,其后无法进行任何元素的逻辑。
用法
stream.peek(e -> {}); stream.peek(e -> peek(Consumer<T> action)); //接一个Consumer表达式
例1:
Stream<Engineer> stream = engineers.stream();
Stream<Engineer> peek =stream.sorted((e1, e2) -> (e1.getAge() - e2.getAge()))
.peek(e -> {
System.out.println("peek -->" + e);
e.setAge(1000); //测试 peek 对象循环修改会真实修改对象
}); //有返回值 Stream
Integer ageSum = peek.map(Engineer::getAge).reduce(Integer::sum).get(); //消费了 stream
System.out.println("ageSum -->"+ageSum);
try {
stream.forEach(e -> System.out.println(e)); //此处会报错因为stream已被消费再次使用会出现错误
} catch (Exception e) {
e.printStackTrace();
}
engineers.stream().forEach(e -> System.out.println("forEach -->"+e)); //无返回值
返回结果:
peek -->Engineer{name='张三', age=12, salary=4863}
peek -->Engineer{name='李四', age=14, salary=6538}
peek -->Engineer{name='王五', age=15, salary=7757}
peek -->Engineer{name='赵六', age=17, salary=406}
ageSum -->4000
java.lang.IllegalStateException: stream has already been operated upon or closed
.
. 缩减篇幅
.
ntellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
forEach -->Engineer{name='张三', age=1000, salary=4863}
forEach -->Engineer{name='李四', age=1000, salary=6538}
forEach -->Engineer{name='王五', age=1000, salary=7757}
forEach -->Engineer{name='赵六', age=1000, salary=406}
limit | skip 截取前n个|删除前n个
limt 获得前面指定个数元素,skip 删除前面指定个数元素
用法:
stream.limit(Num);//获得0-num个元素
stream.skip(Num);//删除0-num个元素
注意: limit或skip不可小于0,否则会抛出异常IllegalArgumentException
例1:
Stream<Engineer> engineerStream = engineers.stream();
engineerStream.sorted((e1,e2) -> e1.getAge() -e2.getAge())
.forEach(e -> System.out.println(e));
System.out.println("*****************************");
engineerStream = engineers.stream();
engineerStream.sorted((e1,e2) -> e1.getAge() -e2.getAge())
.limit(3).forEach(e -> System.out.println(e));//获得前面4个元素
System.out.println("*****************************");
engineerStream = engineers.stream();
engineerStream.sorted((e1,e2) -> e1.getAge() -e2.getAge())
.skip(3).forEach(e -> System.out.println(e));//扔掉前面4个元素
结果:
Engineer{name='张三', age=12, salary=4013}
Engineer{name='李四', age=14, salary=1152}
Engineer{name='王五', age=15, salary=4836}
Engineer{name='赵六', age=17, salary=835}
*****************************
Engineer{name='张三', age=12, salary=4013}
Engineer{name='李四', age=14, salary=1152}
Engineer{name='王五', age=15, salary=4836}
*****************************
Engineer{name='赵六', age=17, salary=835}
Terminal (输出)
操作 | 含义 |
---|---|
forEach | 循环处理元素 |
forEachOrdered | 有序循环处理元素 |
toArray | 流转为数组 |
reduce | 逻辑计算返回一个对象 |
collect | 集合操作 |
min | 获得最小 |
max | 获得最大 |
count | 获得条数 |
anyMatch | 只要有一个满足则返回true |
allMatch | 全部满足则返回true |
noneMatch | 全部不满足则返回true |
findFirst | 获得流的第一个元素 |
findAny | 获得流的任意一元素 |
iterator | 返回流的迭代器 |
forEachOrdered 有序遍历
有序遍历,对并行流parallelStream
依然有效,解决了ForEach
在并行流下排序会被打乱的问题
例1:
List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
list.stream().forEach(System.out::print);
System.out.println();
System.out.println("===========使用并行流===========");
list.parallelStream().forEach(System.out::print);
System.out.println();
System.out.println("===========使用forEachOrder让其排序===========");
list.parallelStream().forEachOrdered(System.out::print);
输出结果:
12345
===========使用并行流===========
35421
===========使用forEachOrder让其排序===========
12345
toArray 将Stream输出成数组
用法
Stream.toArray(); Stream.toArray(T::new);//T::new 等同于 new T()
例1:
Stream<Engineer> stream = engineers.stream();
Engineer[] engineers = stream.peek(engineer -> engineer.setAge(200)).toArray();
for (Engineer engineer : engineers) {
System.out.println(engineer);
}
返回结果:
Engineer{name='张三', age=200, salary=7416}
Engineer{name='李四', age=200, salary=491}
Engineer{name='王五', age=200, salary=3507}
Engineer{name='赵六', age=200, salary=5509}
reduce
计算汇总,可以对流进行数据的计算逻辑从而得到一个结果。比如数量统计的count
、最大\小max\min
.
reduce有三个重载方法:
Optional<T> reduce(BinaryOperator<T> accumulator);
T reduce(T identity, BinaryOperator<T> accumulator);
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
Optional<T> reduce(BinaryOperator<T> accumulator)
接受一个BinaryOperator<T>
函数,(t1,t2) -> return t1
两个入参一个回参,且回参会带入到第二次运算的t1当中,首次的t1为角标为0的元素。注意其回参为Optional
。
T reduce(T identity, BinaryOperator<T> accumulator);
,与第一种变形相同的是都会接受一个BinaryOperator
接口,不同的是其会接受一个identity
参数,用来指定循环的初始值。如果循环为空,就直接返回该值该方法不会返回Optional,因为该方法不会出现null。
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner)
;参数identity
和accumulator
与第二种的含义相同都是定义一个初始值以后参与accumulator
的计算。区别在第三个参数combiner
,第三个参数同样为BinaryOperator
接口其入参为双元入参,入参类型为accumulator
的返回值类型,它的作用在于并行流的结果汇总时把多个分支的数据进行计算。在串型的stream
中其不会执行,只有在parallelStream
中才会执行。原理如下:
在 sequential(串型)流中为
Fork/Join
模式/** 取出流中的头两个元素然后通过后述的lambda表达式,算出记为 新的t1,紧接着取出流中的第三个元素记为t2。最终将值返回,含义如下: stream -> v1,v2,v3,v4,v... 第一步运行 reduce-> v1(t1),v2(t2) 算出等于vTemp 第二步运行 reduce-> vTemp(t1),v3(t2) 算出等于vTemp 第三步运算 reduce-> vTemp(t1),v4(t2) 算出等于vTemp . . . **/ Optional opt = seqStream.reduce((t1,t2) -> { return T});//返回值为opt,可以使用.orElseGet(() -> lambda)判断后获得结果 //--------------------------------------------------------------------------------- /** 取出startT 和流的第一个元素通过后述的lambda表达式,算出记为 新的t1,紧接着取出流中的第二个元素记为t2。最终将值返回,含义如下: stream -> v1,v2,v3,v4,v... 第一步运行 reduce-> startT(t1),v1(t2) 算出等于vTemp 第二步运行 reduce-> vTemp(t1),v2(t2) 算出等于vTemp 第三步运算 reduce-> vTemp(t1),v3(t2) 算出等于vTemp **/ T t = seqStream.reduce(startT,(t1,t2) -> {return T}); //返回值为 T
在 parallel(并型)流中
/** 取出流中的两个元素然后通过后述的lambda表达式,算出记为结果r1,同时另一条线程获得流中的另外两个元素通过计算得r2,最后将所有得结果进行递归合并得r1+r2+...(注意:此处不可单纯认为是r1+r2..,其类似于JDK1.7得Fork/join 模式将任务分给多个线程去运行然后各个线程内部合并运算后最终外部再进行合并运算),含义如下 原有元素: ->v1,v2,v3,v4,v... stream--Thread1 -> v1,v2,... stream--Thread2 -> v3,v5,... stream--Thread3 -> v4,v5,... ... more 第一步运行 Thread1 - reduce -> v1(t1),v2(t2) 算出等于r1 同时 Thread2 - reduce -> v3(t1),v5(t2) 算出等于r2 ...more 第二步运行 Thread1 - reduce -> v18(t1),r1(t2) 算出等于r1 (此处得v18为假设) 同时 Thread2 - reduce -> v9(t1),r2(t2) 算出等于r2 (此处得v5为假设) ...more . . . 第n步运算 reduce-> r1(t1),r2(t2) 算出等于 result **/ Optional opt = parStream.reduce((t1,t2) -> { return T});//返回值为opt,可以使用.orElseGet(() -> lambda)判断后获得结果 //--------------------------------------------------------------------------------- /** 取出流中的1个元素然后与startT通过后述的lambda表达式,算出记为结果r1,同时另一条线程获得流中的另外1个元素与startT通过计算得r2,后多条线程又去流中各自获取一个元素与startT进行运算得出结果r3,r4,r5...,最后将所有得结果进行递归合并得r1+r2+...(注意:此处不可单纯认为是r1+r2..,其类似于JDK1.7得Fork/join 模式将任务分给多个线程去运行然后各个线程内部合并运算后最终外部再进行合并运算),含义如下 原有元素: ->v1,v2,v3,v4,v... stream--Thread1 -> v1,v2,... stream--Thread2 -> v3,v5,... stream--Thread3 -> v4,v5,... ... more 第一步运行 Thread1 - reduce -> startT(t1),v2(t2) 算出等于threadResult1[0] = r1 同时 Thread2 - reduce -> startT(t1),v3(t2) 算出等于threadResult2[0] = r2 ...more 第二步运行 Thread1 - reduce -> startT(t1),v18(t2) 算出等于threadResult1[1] = r18 同时 Thread2 - reduce -> startT(t1),v5(t2) 算出等于threadResult2[1] = r5 ...more . . . 第n步运算 reduce-> 算出等于threadResult1[0]+hreadResult1[1]+...+hreadResult2[0]+hreadResult2[1]... 算出等于 result **/ T t = parStream.reduce(startT,(t1,t2) -> {return T});//返回值为 T
区别: 并行流时,起始数将会被计算 stream的长度次。而在串行时其只会被计算一次。由上述过程也可得知,在处理逻辑复杂并涉及到其他元素的结果时请慎重考虑使用并行计算。
基本类型可使用的函数列表
函数 | 含义 |
---|---|
Number::min | 最小 |
Number::max | 最大 |
Number::sum | 求和 |
String::concat | 字符串拼接 |
例1:单个参数
从并行流的返回结果可以看出它是把1、2给了线程1,3、4给了线程2去执行的。
List<Integer> integers = Lists.newArrayList(1, 2, 3, 4);
AtomicInteger atomicInteger = new AtomicInteger(0);
BinaryOperator<Integer> operator = new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer t1, Integer t2) {
System.out.println("*******第:" + atomicInteger.addAndGet(1) + "次调用*******");
System.out.println("t1=>" + t1);
System.out.println("t2=>" + t2);
t1 = t1 + t2;
return t1;
}
};
System.out.println("[sequential]==>"+integers.stream().reduce(operator).get());
atomicInteger.set(0);
System.out.println("[parallel]==>"+integers.parallelStream().reduce(operator).get());
返回结果:
*******第:1次调用*******
t1=>1
t2=>2
*******第:2次调用*******
t1=>3
t2=>3
*******第:3次调用*******
t1=>6
t2=>4
[sequential]==>10
*******第:1次调用*******
t1=>3
t2=>4
*******第:2次调用*******
t1=>1
t2=>2
*******第:3次调用*******
t1=>3
t2=>7
[parallel]==>10
例2:有初始值
给了串行和并行流一样的起始值5
,但是发现两个函数的值不一样一个是15
一个是30
.
分析查看并行流的执行过程发现其有四个使用了起始值的表达式分别是:5+3
,5+4
,5+2
,5+1
。而串行的只有一个使用了起始值5+1
;从中可以看出并行流如果有起始值其结果将与预想的结果有差异.
List<Integer> integers = Lists.newArrayList(1, 2, 3, 4);
AtomicInteger atomicInteger = new AtomicInteger(0);
BinaryOperator<Integer> operator = new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer t1, Integer t2) {
System.out.println("*******第:" + atomicInteger.addAndGet(1) + "次调用*******");
System.out.println("t1=>" + t1);
System.out.println("t2=>" + t2);
t1 = t1 + t2;
return t1;
}
};
System.out.println("[sequential]==>"+integers.stream().reduce(5,operator));
atomicInteger.set(0);
System.out.println("[parallel]==>"+integers.parallelStream().reduce(5,operator));
返回结果:
*******第:1次调用*******
t1=>5
t2=>1
*******第:2次调用*******
t1=>6
t2=>2
*******第:3次调用*******
t1=>8
t2=>3
*******第:4次调用*******
t1=>11
t2=>4
[sequential]==>15
*******第:1次调用*******
t1=>5
t2=>3
*******第:2次调用*******
t1=>5
t2=>4
*******第:3次调用*******
t1=>5
t2=>2
*******第:4次调用*******
t1=>5
t2=>1
*******第:6次调用*******
t1=>6
t2=>9
*******第:5次调用*******
t1=>8
t2=>7
*******第:7次调用*******
t1=>13
t2=>17
[parallel]==>30
例3:并发归并
使用并行流运行以后自行制定对多条线程的合并操作流程。
List<Integer> integers = Lists.newArrayList(1, 2, 3, 4);
AtomicInteger atomicInteger = new AtomicInteger(0);
List<Integer> result = integers.parallelStream().reduce(
Lists.newArrayList(100),
new BiFunction<List<Integer>, Integer, List<Integer>>() {
@Override
public List<Integer> apply(List<Integer> list, Integer item) {
System.out.println("*******第:" + atomicInteger.addAndGet(1) + "次调用[accumulator]*******");
System.out.println("t1=>" + list);
System.out.println("t2=>" + item);
List<Integer> integerList = Lists.newArrayList();
integerList.addAll(list);
integerList.add(item);
return integerList;
}
},
new BinaryOperator<List<Integer>>() {
@Override
public List<Integer> apply(List<Integer> node1, List<Integer> node2) {
System.out.println("combiner");
System.out.println("node1=>" + node1);
System.out.println("node2=> " + node2);
List<Integer> integerList = Lists.newArrayList();
integerList.addAll(node1);
integerList.addAll(node2);
return integerList;
}
});
System.out.println(result);
返回结果:
*******第:1次调用[accumulator]*******
t1=>[100]
t2=>3
*******第:2次调用[accumulator]*******
t1=>[100]
t2=>4
combiner
node1=>[100, 3]
node2=> [100, 4]
*******第:3次调用[accumulator]*******
t1=>[100]
t2=>2
*******第:4次调用[accumulator]*******
t1=>[100]
t2=>1
combiner
node1=>[100, 1]
node2=> [100, 2]
combiner
node1=>[100, 1, 100, 2]
node2=> [100, 3, 100, 4]
[100, 1, 100, 2, 100, 3, 100, 4]
例4:内置函数:
List<Integer> integers = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println("默认最小值==>"+integers.stream().parallel().reduce(Integer::min).get());
System.out.println("带参判断最小值==>"+integers.stream().parallel().reduce(0,Integer::min));
System.out.println("默认最大值==>"+integers.stream().parallel().reduce(Integer::max).get());
System.out.println("带参判断最大值==>"+integers.stream().parallel().reduce(Integer.MAX_VALUE,Integer::max));
System.out.println("求和计算==>"+integers.stream().parallel().reduce(Integer::sum));
返回结果:
默认最小值==>1
带参判断最小值==>0
默认最大值==>9
带参判断最大值==>2147483647
求和计算==>Optional[45]v
Collects
Collectors是java.util.stream
下的一个工具类,其内部提供了多个返回值为Collector
的方法,我们可以直接将其拿来使用。
Collector是java.util.stream
下的一个接口
用法
stream.collect(Collectors c);//内部可以接 java.util.stream.Collectors 中的方法
已知方法列表:
- toList stream转List集合
- toSet stream转Set集合
- toMap stream转map集合
- joining 字符串符号拼接
- maxBy\minBy 取最大最小
- summarizingInt 汇总
- averagingInt 平均数 counting数量
- groupingBy 分组
- partitioningBy 根据true\false分组
- reducing 数据归约
- collectingAndThen 归集然后再操作
- mapping元素提取后通过归集去搜集
函数 | 含义 |
---|---|
指定key转为Map | |
数据汇总 | |
操作后接着怎么做 |
toList stream转List集合
例子:10633
List<Engineer> collectList = enginnerList.stream().collect(Collectors.toList());
System.out.println("流转[List]集合(toList)==>"+ JSON.toJSONString(collectList));
结果:
流转[List]集合(toList)==>[{"age":50,"name":"张三","salary":50000.00},
{"age":22,"name":"李四","salary":4900.00},
{"age":43,"name":"王五","salary":8000.00},
{"age":18,"name":"赵六","salary":6000.00},
{"age":36,"name":"陈七","salary":14000.00}]
toSet stream转Set集合
例子:
System.out.println("流转[Set]集合(toSet)==>"+ JSON.toJSONString(enginnerList.stream().collect(Collectors.toSet())));
System.out.println("=== 去重转Set ===");
//此处的set去重用到了Comparator.comparing() 方法
TreeSet<Engineer> collect = enginnerList.stream().collect(Collectors.toCollection(() -> Sets.newTreeSet(Comparator.comparing(Engineer::getAge))));
System.out.println("流转[Set]集合去重(toCollection)==>"+ JSON.toJSONString(collect));
输出:
流转[Set]集合(toSet)==>[
{"age":43,"name":"王五","salary":8000.00},
{"age":50,"name":"张三","salary":50000.00},
{"age":22,"name":"陈七","salary":14000.00},
{"age":22,"name":"李四","salary":4900.00},
{"age":18,"name":"赵六","salary":6000.00}]
=== 去重转Set ===
流转[Set]集合去重(toCollection)==>[
{"age":18,"name":"赵六","salary":6000.00},
{"age":22,"name":"李四","salary":4900.00},
{"age":43,"name":"王五","salary":8000.00},
{"age":50,"name":"张三","salary":50000.00}]
toMap stream转map集合
将 stream
转成map
集合。提供三个函数如下:
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);
keyMapper: key的映射,valueMapper:value的映射,
mergeFunction: 当value冲突时,调用的value合并的方法,mapSupplier map的构造器
例子1:
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
//根据name转成map
userList.stream().collect(Collectors.toMap(User::getName, t -> t))
.forEach((k,v) -> System.out.println("k1—>"+k+"v1->"+v.toString()));
输出:
k1—>李四v1->User(id=3, name=李四, age=12, salary=10)
k1—>张三v1->User(id=1, name=张三, age=10, salary=10)
k1—>王五v1->User(id=4, name=王五, age=10, salary=10)
k1—>陈七v1->User(id=5, name=陈七, age=10, salary=10)
k1—>赵六v1->User(id=2, name=赵六, age=14, salary=10)
例2:
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator mergeFunction);
//根据age将salary汇总
userList.stream().collect(Collectors.toMap(User::getAge, Function.identity(),(t1,t2) -> {
t1.setSalary(t1.getSalary().add(t2.getSalary()));
t1.setName(t1.getName()+"-"+t2.getName());
return t1;
})).forEach((k,v) -> System.out.println("k—>"+k+"v->"+v.toString()));
输出:
k—>10v->User(id=1, name=张三-王五-陈七, age=10, salary=30)
k—>12v->User(id=3, name=李四, age=12, salary=10)
k—>14v->User(id=2, name=赵六, age=14, salary=10)
例3:
toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);
//根据 id 进行排序
userList.stream().collect(
Collectors.toMap(User::getId, Function.identity(), (t1, t2) -> t1, TreeMap::new)
).forEach((k,v) -> System.out.println("k3—>"+k+"v3->"+v.toString()));
输出:
k—>1v->User(id=1, name=张三, age=10, salary=10)
k—>2v->User(id=2, name=赵六, age=14, salary=10)
k—>3v->User(id=3, name=李四, age=12, salary=10)
k—>4v->User(id=4, name=王五, age=10, salary=10)
k—>5v->User(id=5, name=陈七, age=10, salary=10)
joining 字符串符号拼接
将字符串集合铜通过指定字符去拼接,提供两种函数,如下:
Collector<CharSequence, ?, String> joining(CharSequence delimiter);
// 拼接字 前缀 后缀
Collector<CharSequence, ?, String> joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)
例:
Assertions.assertEquals(userList.stream().map(User::getName)
.collect(Collectors.joining("%")),"张三%李四%王五%赵六%陈七");
String collect = userList.stream().map(User::getName)
.collect(Collectors.joining("%", "prefix", "suffix"));
Assertions.assertEquals(collect,"prefix张三%李四%王五%赵六%陈七suffix");
maxBy\minBy 取最大最小
Assertions.assertEquals(
userList.stream().max(Comparator.comparingInt(User::getAge)).get().getName(),"赵六");
Assertions.assertEquals(
userList.stream().min(Comparator.comparingInt(User::getAge)).get().getName(),"张三");
summarizing 汇总
汇总一个属性的运算,有summarizingDouble
、summarizingLong
、summarizingInt
。与MaxBy\MinBy
的区别是这个只能处理单个属性的。其入参是一个FunctionAverage
、个数Count
、最大值Max
、最小值Min
、和Sum
。
summarizingInt(ToIntFunction<? super T> mapper)
例:
IntSummaryStatistics collect = Stream.of(1,2,3,4,5,6,7).collect(Collectors.summarizingInt(t -> t));
Assertions.assertEquals(collect.getAverage(),4);
Assertions.assertEquals(collect.getCount(),7);
Assertions.assertEquals(collect.getMax(),7);
Assertions.assertEquals(collect.getMin(),1);
Assertions.assertEquals(collect.getSum(),28);
averaging 平均数 counting 数量
averaging
相当于 summarizing
的getAverage
函数,
counting
相当于summarizing
的getCount
函数,不过其不是算的某个字段而是所有。
Assertions.assertEquals(userList.stream().collect(Collectors.averagingInt(User::getAge)),11.2);
Assertions.assertEquals(Stream.of(1, 2, 3, 4, 5, 6, 7).collect(Collectors.counting()),7);
groupingBy 分组
将流根据指定属性进行分组,与toMap
不一样的地方在于其value是一个Collectors
可以做更多的逻辑。提供以下三种函数供使用:
//根据指定属性进行分组
Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> function);
//根据指定属性进行分组,将value使用collector进行处理
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> function,Collector<? super T, A, D> collector);
//可以自行指定输出的map类型
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream)
例:
//根据指定属性 age 去分组
Map<Integer, List<User>> userMap = userList.stream().collect(Collectors.groupingBy(User::getAge));
userMap.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));
//根据指定属性 name+age 去分组
System.out.println("-------------条件重定义:根据指定属性name+age去分组-------------");
Map<String, List<User>> userMap1 = userList.stream().collect(
Collectors.groupingBy(t -> t.getName() + "&" + t.getAge()));
userMap1.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));
//条件重定义。根据age大于10和小于等于10分组
System.out.println("-------------条件重定义:根据age大于10和小于等于10分组-------------");
Map<String, List<User>> userMap2 = userList.stream().collect(
Collectors.groupingBy(t -> {
if (t.getAge() > 10) {
return "青少年";
}
return "少年";
}));
userMap2.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));
//多条件分组. 根据age大于10和小于等于10分组,再获得各自的男女
System.out.println("-------------多条件分组:根据age大于10和小于等于10分组-------------");
Map<String, Map<String, List<User>>> userMap3 = userList.stream().collect(
Collectors.groupingBy(
//条件1:根据age大于10和小于等于10分组
t -> {
if (t.getAge() > 10) {
return "青少年";
}
return "少年";
},
//条件2:根据男女再次分组
Collectors.groupingBy(User::getSex)
)
);
userMap3.forEach((k1, v) -> {
v.forEach((k2, v2) -> System.out.println("k1->" + k1 + ",k2->" + k2 + ",v->" + v2));
});
//结果集的聚合函数,判断男女的人数
System.out.println("-------------结果集的聚合函数,判断男女的人数-------------");
Map<String, Long> userMap4 = userList.stream().collect(
Collectors.groupingBy(User::getSex, Collectors.counting()
);
userMap4.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));
//提供指定的Map构造函数:根据age分组并根据age升序
System.out.println("-------------提供指定的Map构造函数:根据age分组并根据age降序-------------");
TreeMap<Integer, List<User>> userMap5 = userList.stream().collect(
Collectors.groupingBy(User::getAge,
//根据age降序
() -> new TreeMap<Integer, List<User>>(Comparator.comparing(t-> -t)),
Collectors.toList()));
userMap5.forEach((k, v) -> System.out.println("k->" + k + ",v->" + v));
输出:
k->6,v->[User(id=5, name=陈七, age=6, salary=10, sex=男)]
k->10,v->[User(id=1, name=张三, age=10, salary=10, sex=男), User(id=4, name=王五, age=10, salary=10, sex=女)]
k->12,v->[User(id=3, name=李四, age=12, salary=10, sex=女)]
k->14,v->[User(id=2, name=赵六, age=14, salary=10, sex=女)]
-------------条件重定义:根据指定属性name+age去分组-------------
k->王五&10,v->[User(id=4, name=王五, age=10, salary=10, sex=女)]
k->李四&12,v->[User(id=3, name=李四, age=12, salary=10, sex=女)]
k->赵六&14,v->[User(id=2, name=赵六, age=14, salary=10, sex=女)]
k->张三&10,v->[User(id=1, name=张三, age=10, salary=10, sex=男)]
k->陈七&6,v->[User(id=5, name=陈七, age=6, salary=10, sex=男)]
-------------条件重定义:根据age大于10和小于等于10分组-------------
k->青少年,v->[User(id=3, name=李四, age=12, salary=10, sex=女), User(id=2, name=赵六, age=14, salary=10, sex=女)]
k->少年,v->[User(id=1, name=张三, age=10, salary=10, sex=男), User(id=4, name=王五, age=10, salary=10, sex=女), User(id=5, name=陈七, age=6, salary=10, sex=男)]
-------------多条件分组:根据age大于10和小于等于10分组-------------
k1->青少年,k2->女,v->[User(id=3, name=李四, age=12, salary=10, sex=女), User(id=2, name=赵六, age=14, salary=10, sex=女)]
k1->少年,k2->女,v->[User(id=4, name=王五, age=10, salary=10, sex=女)]
k1->少年,k2->男,v->[User(id=1, name=张三, age=10, salary=10, sex=男), User(id=5, name=陈七, age=6, salary=10, sex=男)]
k->女,v->3
k->男,v->2
partitioningBy 根据true\false分组
例:
//根据指定属性 age 去分组
Map<Boolean, List<Integer>> map = Stream.of(1, 2, 3, 4, 5, 6, 7).collect(
Collectors.partitioningBy(t -> t % 2 == 0));
Assertions.assertEquals(map.get(true).toString(),Lists.newArrayList(2,4,6).toString());
mapping 将指定的属性取出,带入后续做collectors的传入
Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper,Collector<? super U, A, R> downstream)
例:
String collect = userList.stream().collect(Collectors.mapping(User::getSex, Collectors.joining("&")));
Assertions.assertEquals(collect,"男&女&女&女&男");
reducing 数据归约
将流的节点通过规则进行聚合,提供如下三种函数
//传入一个BiFunction,其元素1的元素将会带入到后续中进行计算
Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op)
//传入一个默认元素,默认元素将会带入到后续进行计算
Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op)
//传入一个默认元素,传入一个 mapper 其会将节点中的每个元素都参与处理,之后会带入到后续进行计算
Collector<T, ?, U> reducing(U identity,
Function<? super T, ? extends U> mapper,
BinaryOperator<U> op)
例1:
算出所有元素的累计之和
Collector<T, ?, Optional
Assertions.assertEquals(Stream.of(1,2,3,4,5,6).collect(Collectors.reducing((t1,t2) -> t1+t2)).get(),21);
过程分析:
1,2 -> return 1+2
3,3 -> return 3+3
6,4 -> return 6+4
.....
例2:
传入一个默认元素然后将其带入到后面进行运算
Collector<T, ?, T> reducing(T identity, BinaryOperator
Assertions.assertEquals(
Stream.of(1,2,3,4,5,6).collect(Collectors.reducing(100,(t1,t2) -> t1+t2)),121)
例3:
传入一个默认元素,传入一个 mapper 其会将节点中的每个元素都参与处理,之后会带入到后续进行计算
Collector<T, ?, U> reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator op)
Assertions.assertEquals(
Stream.of(1,2,3,4,5,6).collect(Collectors.reducing(0,t -> t+1,(t1,t2) -> t1+t2)),27);
过程分析:
identity : 获得一个默认值 0
mapper: 将1 带入得 2
op: 0,2 -> return 0+2
mapper: 将2 带入得 3
op:2,3 -> return 2+3
mapper: 将3 带入得 4
op:5,4 -> return 5+4
。。。
collectingAndThen 归集然后再操作
将流的元素通过collectors
转化以后再通过Function
进行操作,提供如下函数
Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher)
例1:
汇总 少年和青年
的男女
分别的薪水总额
Map<String, Double> result = userList.stream().collect(Collectors.collectingAndThen(
Collectors.groupingBy(t -> {
if (t.getAge() > 10) {
return "青少年";
}
return "少年";
}, Collectors.groupingBy(User::getSex)),
//Map<String, Map<String, List<User>>>
e -> {
Map<String, Double> map = Maps.newHashMap();
e.forEach((k, v) -> {
//每一组用户
v.forEach((k1, v1) -> {
double sum = v1.stream().map(User::getSalary).collect(Collectors.summarizingDouble(BigDecimal::doubleValue)).getSum();
map.put(k + "-" + k1, sum);
});
});
return map;
}
));
result.forEach((k, v) -> System.out.println("k->" + k + "v->" + v));
结果:
k->少年-男v->20.0
k->青少年-男v->40.0
k->青少年-女v->30.0
例2:
男女根据年龄age降序
Map<String, List<User>> collect = userList.stream().collect(Collectors.collectingAndThen(
Collectors.groupingBy(User::getSex),
e -> {
e.values().forEach(t -> t.sort(Comparator.comparing(User::getAge).reversed()));
return e;
}
));
collect.forEach((k,v) -> v.forEach(v1 -> System.out.println("k->" + k + "v->" + v1)));
结果:
k->女v->User(id=4, name=王五, age=18, salary=10, sex=女)
k->女v->User(id=2, name=赵六, age=14, salary=10, sex=女)
k->女v->User(id=3, name=李四, age=12, salary=10, sex=女)
k->男v->User(id=5, name=洪税, age=43, salary=10, sex=男)
k->男v->User(id=5, name=张三丰, age=23, salary=10, sex=男)
k->男v->User(id=5, name=张无忌, age=16, salary=10, sex=男)
k->男v->User(id=5, name=洪七公, age=14, salary=10, sex=男)
k->男v->User(id=1, name=张三, age=10, salary=10, sex=男)
k->男v->User(id=5, name=陈七, age=6, salary=10, sex=男)
例3:
去重只保留对应的age的用户
List<User> collect1 = userList.stream().collect(Collectors.collectingAndThen(
Collectors.toMap(User::getAge, identity(), (t1, t2) -> t1, Maps::newHashMap),
e -> Lists.newArrayList(e.values())
));
collect1.forEach(System.out::println);
结果:
User(id=5, name=张无忌, age=16, salary=10, sex=男)
User(id=4, name=王五, age=18, salary=10, sex=女)
User(id=5, name=陈七, age=6, salary=10, sex=男)
User(id=5, name=张三丰, age=23, salary=10, sex=男)
User(id=1, name=张三, age=10, salary=10, sex=男)
User(id=5, name=洪税, age=43, salary=10, sex=男)
User(id=3, name=李四, age=12, salary=10, sex=女)
User(id=2, name=赵六, age=14, salary=10, sex=女)
min | max
用法:
Optional opt = stream.min((v1,v2) -> return Integer); Optional opt = stream.max((v1,v2) -> return Integer);
e.g.:
Stream 中只要有一个元素符合传入的 predicate,返回 trueEngineer engineerMin = engineers.stream().min((e1, e2) -> e1.getAge() - e2.getAge()).get();
System.out.println("[min]获得最小 ==>"+engineerMin);
Engineer engineerMax = engineers.stream().max((e1, e2) -> e1.getAge() - e2.getAge()).get();
System.out.println("[max]获得最大 ==>"+engineerMax);
返回结果:
[min]获得最小 ==>Engineer{name='李四', age=14, salary=5000}
[max]获得最大 ==>Engineer{name='赵六', age=20, salary=20000}
anyMatch
用法:
stream.anyMatch(T t -> {return true|false});
Stream 中只要有一个元素符合传入的 Predicate 表达式,则返回 true
e.g.:
final String engineerName="张三";
boolean b = engineers.stream().anyMatch(engineer -> engineerName.equals(engineer.getName()));
if (b) {
System.out.println("["+engineerName+"]在集合中");
}
结果:
[张三]在集合中
allMatch | noneMatch
用法:
stream.allMatch(enginner -> {return true|false});//全为true则返true,否者为false stream.noneMatch(enginner -> {return true|false});//全为false则返回true,否者为true
e.g.:
Map engineerMap = new HashMap<String,Engineer>(engineers.size());
engineers.forEach(engineer -> engineerMap.put(engineer.getName(),engineer));
Set<String> SetKey = engineerMap.keySet();
boolean b = engineers.parallelStream().allMatch(engineer -> SetKey.contains(engineer.getName()));
if (b) {
System.out.println("集合所有元素在新集合中都拥有");
}
//重置map
Set<String> finalSetKey = new HashMap<String,Engineer>(engineers.size()).keySet();
b = engineers.parallelStream().noneMatch(engineer -> finalSetKey.contains(engineer.getName()));
if (b) {
System.out.println("集合所有元素在新集合中都[不]拥有");
}
结果:
集合所有元素在新集合中都拥有
集合所有元素在新集合中都[不]拥有
findFirst | findAny
用法:
Optional opt = stream.findFirst();//获得流的第一个元素返回Optional Optional opt = stream.findAny();//获得流(并行流会有变化)的任意元素返回Optional
e.g.:
Optional<Engineer> first = engineers.parallelStream().findFirst();
System.out.println("使用[findFirst] ==>"+first.get());
Optional<Engineer> any = engineers.parallelStream().findAny();
System.out.println("使用[findAny] ==>"+any.get());
结果:
使用[findFirst] ==>Engineer{name='李四', age=14, salary=5000}
使用[findAny] ==>Engineer{name='王五', age=15, salary=3000}
Short-circuiting:
操作 | 含义 |
---|---|
anyMatch | 一个符合则跳出为True |
noneMatch | 全部为false则为True |
findAny | 获得任一 |
allMatch | 全部为true则为true |
findFirst | 获得第一个 |
limit | 截取前n个 |
Optional
对象校验的一个包装类,包地址 java.util.Optional
ofNullable | of
Optional 参数的创建需要时用 ofNullable 或者of 方法来创建,两者都会返回一个 Optional 对象。其区别是:
ofNullable 在校验对象为空时不会抛错,常使用此方式来创建Optional对象,而 of 会直接返回
NullPointerException
异常
e.g.:
Engineer engineer = null;
Optional<Engineer> engineer1 = Optional.ofNullable(engineer);
Optional<Engineer> engineer2 = Optional.of(engineer);
结果:
java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at java.util.Optional.<init>(Optional.java:96)
at java.util.Optional.of(Optional.java:108)
...
get
获取Optional的值
e.g.:
Engineer engineer = new Engineer("李四", 14, new BigDecimal("5000"));
Optional<Engineer> engineer1 = Optional.ofNullable(engineer);
System.out.println(engineer1.get());
结果:
Engineer{name='李四', age=14, salary=5000}
ifPresent
如果 Optional 有效则会执行ifPresent 中的方法
optional.ifPresent(T t -> {});//此处的t 是校验对象
e.g.:
Engineer engineer = new Engineer("李四", 14, new BigDecimal("5000"));
Optional.ofNullable(engineer).ifPresent(engineer1 -> engineer.setAge(20));
System.out.println(engineer);
结果:
Engineer{name='李四', age=20, salary=5000}
orElse
optional 校验对象为空时,其会执行此方法里面的内容,其返回值要与校验对象一致
optional.orElse(return T t);
e.g.:
Engineer engineer = null;
engineer = Optional.ofNullable(engineer).orElse(new Engineer("李四", 14, new BigDecimal("5000")));
System.out.println(engineer);
结果:
Engineer{name='李四', age=14, salary=5000}
orElseGet
optional 校验对象为空时,其会执行此方法里面的内容,其返回值要与校验对象一致.
与
OrElse
的区别是,orElseGet 接一个lambda表达式,表达式默认不执行只在需要的时候被执行,而 orElse 会注定执行optional.ofElseGet(Supplier sup);//接Supplier 函数接口
e.g.:
Engineer engineer = null;
engineer = Optional.ofNullable(engineer).orElseGet(() -> new Engineer("张三", 12, new BigDecimal("20.5")));
System.out.println(engineer);
结果:
Engineer{name='张三', age=12, salary=20.5}
orElseThrow
前者没达到得情况下会激活抛出错误.
optional.orElseThrow(() -> return Exception);
e.g.:
Engineer engineer = null;
engineer = Optional.ofNullable(engineer)
.orElseThrow(
() -> new RuntimeException("报错了哦[空指针]!")
);
System.out.println(engineer);
结果:
java.lang.RuntimeException: 报错了哦[空指针]!
at OptionalTest.lambda$test2$1(OptionalTest.java:44)
at java.util.Optional.orElseThrow(Optional.java:290)
map
filter
过滤器,传入一个 Predicate 函数接口,成功则会成功返回Optional,否者返回一个空的 Optional
Optional opt = optional.filter(e -> {return true|false});
e.g.:
ArrayList statusList = new ArrayList<String>();
statusList = Optional.ofNullable(statusList)
//返回一个 null的optional
.filter(statuses -> !(statuses == null || statuses.isEmpty()))
//因为上文返回的null 的optional 则 orElseGet 会被执行填入参数
.orElseGet(() -> {
ArrayList arrayList = new ArrayList();
arrayList.add("张三");
arrayList.add("李四");
return arrayList;
});
System.out.println(statusList);
结果:
[张三, 李四]