Stream 详解
什么是流?
流是Java8引入的全新概念,它用来处理集合中的数据,暂且可以把它理解为一种高级集合。众所周知,集合操作非常麻烦,若要对集合进行筛选、投影,需要写大量的代码,而流是以声明的形式操作集合,它就像SQL语句,我们只需告诉流需要对集合进行什么操作,它就会自动进行操作,并将执行结果交给你,无需我们自己手写代码。因此,流的集合操作对我们来说是透明的,我们只需向流下达命令,它就会自动把我们想要的结果给我们。由于操作过程完全由Java处理,因此它可以根据当前硬件环境选择最优的方法处理,我们也无需编写复杂又容易出错的多线程代码了。
流的特点
- 只能遍历一次 我们可以把流想象成一条流水线,流水线的源头是我们的数据源(一个集合),数据源中的元素依次被输送到流水线上,我们可以在流水线上对元素进行各种操作。一旦元素走到了流水线的另一头,那么这些元素就被“消费掉了”,我们无法再对这个流进行操作。当然,我们可以从数据源那里再获得一个新的流重新遍历一遍。
- 采用内部迭代方式 若要对集合进行处理,则需我们手写处理代码,这就叫做外部迭代。而要对流进行处理,我们只需告诉流我们需要什么结果,处理过程由流自行完成,这就称为内部迭代。
流的操作种类
流的操作分为两种,分别为中间操作和终端操作。
- 中间操作 当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为“中间操作”。中间操作仍然会返回一个流对象,因此多个中间操作可以串连起来形成一个流水线。
- 终端操作 当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行终端操作。终端操作将返回一个执行结果,这就是你想要的数据。
流的操作过程
使用流一共需要三步:
- 准备一个数据源
- 执行中间操作 中间操作可以有多个,它们可以串连起来形成流水线。
- 执行终端操作 执行终端操作后本次流结束,你将获得一个执行结果。
Stream 接口一览
list转Stream
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6);
//转Stream
Stream<Integer> stream = integers.stream();
//并发处理
Stream<Integer> integerStream = integers.parallelStream();
转list、collect(转换成集合)
// 转list
Collectors.toList();
// 转set
Collectors.toSet();
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
例子:将List转换为Strem,然后再转换List
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6);
//转Stream
List<Integer> collect = integers.stream().collect(Collectors.toList());
filter(过滤)
filter 函数接收一个Lambda表达式作为参数,该表达式返回boolean,在执行过程中,流将元素逐一输送给filter,并筛选出执行结果为true的元素。
Stream<T> filter(Predicate<? super T> predicate);
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//filter
List<Integer> filterList = integerAry.stream()
.filter(item -> item>3)
.collect(Collectors.toList());
for (Integer i : filterList){
System.out.println("filter: "+i);
}
map(元素转换)
对流中的每个元素执行一个函数,使得元素转换成另一种类型输出。流会将每一个元素输送给map函数,并执行map中的Lambda表达式,最后将执行结果存入一个新的流中。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
例子:获取列表每一个值乘以2
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//map
List<Integer> mapColl = integerAry.stream()
.map(item -> item*2)
.collect(Collectors.toList());
for (Integer i : mapColl){
System.out.println("map: "+i);
}
结果
distinct(去除重复,对象需要重写 equals、hashCode)
Stream<T> distinct();
例子:
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//distinct去重
List<Integer> distinctList = integerAry.stream()
.distinct()
.collect(toList());
for (Integer i : distinctList){
System.out.println("distinct: "+i);
}
结果为:
sorted(排序)
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//sorted排序
List<Integer> sortedList = integerAry.stream()
.sorted()
.collect(toList());
for (Integer i : sortedList){
System.out.println("sorted: "+i);
}
//sorted倒序
List<Integer> reversesortedList = integerAry.stream()
.sorted(Comparator.reverseOrder())
.collect(toList());
for (Integer i : reversesortedList){
System.out.println("reversesortedList: "+i);
}
结果:
peek(生成新的流:流是单向的,例如用于日志打印)
peek方法接收一个Consumer的入参. 了解λ表达式的应该明白 Consumer的实现类应该只有一个方法,该方法返回类型为void. 它只是对Stream中的元素进行某些操作,但是操作之后的数据并不返回到Stream中,所以Stream中的元素还是原来的元素.
Stream<T> peek(Consumer<? super T> action);
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
List<Integer> peekList = integerAry.stream()
.peek(item -> System.out.println("peek: "+(item+2)))
.collect(toList());
for (Integer i: peekList){
System.out.println("peekList: "+ i);
}
limit(取前面 n
个元素)
Stream<T> limit(long maxSize);
例子:截取List的前3个元素
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//limit
List<Integer> limitList = integerAry.stream()
.limit(3)
.collect(toList());
for (Integer i:limitList){
System.out.println("limit: "+i);
}
结果:
skip(跳过 n
个元素)
Stream<T> skip(long n);
例子:跳过list的前3个元素
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//skip
List<Integer> skipList = integerAry.stream()
.skip(3)
.collect(toList());
for (Integer i:skipList){
System.out.println("skip: "+i);
}
forEach(遍历)
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//foreach
integerAry.stream()
.forEach(item-> System.out.println("forEach: "+item));
toArray(转换成数组)
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
例子:
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
Object[] toArray = integerAry.stream()
.filter(item -> item > 2)
.toArray();
//toArray
for(Object i:toArray){
System.out.println("toArray: "+i);
}
flatMap(元素转换)
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
List<String> list = new ArrayList<String>();
list.add("I am a boy");
list.add("I love the girl");
list.add("But the girl loves another girl");
//flatMap
List<Object> flatMapAry = list.stream()
.map(line -> line.split(" "))//按空格分词每个元素变成了一个String[]数组
.flatMap(Arrays::stream)//此时一个大流里面包含了一个个小流,我们需要将这些小流合并成一个流。 将小流合并成一个大流:用 flatMap 完成
.distinct()
.collect(toList());
for (Object i: flatMapAry){
System.out.println("flatMap: "+i);
}
结果:
anyMatch是否匹配任一元素
anyMatch用于判断流中是否存在至少一个元素满足指定的条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//anyMatch
boolean b = integerAry.stream()
.anyMatch(item -> item > 3);
System.out.println("anyMatch: "+b);
结果为:
allMatch(是否匹配所有元素)
allMatch用于判断流中的所有元素是否都满足指定条件,这个判断条件通过Lambda表达式传递给anyMatch,执行结果为boolean类型。
查看list是否全部大于0
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//anyMatch
boolean allMatchB = integerAry.stream()
.allMatch(item -> item > 0);
System.out.println("allMatch: "+allMatchB);
noneMatch(是否未匹配所有元素)
noneMatch与allMatch恰恰相反,它用于判断流中的所有元素是否都不满足指定条件:
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
boolean nnotallow= integerAry.stream()
.noneMatch(item -> item > 0);
System.out.println("noneMatch: "+noneMatchB);
结果:
findAny(获取任一元素)
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//获取任一元素
Optional<Integer> any = integerAry.stream()
.findAny();
if(any.isPresent()){
System.out.println("findAny: "+ any.get());
}
结果:
findFirst(获取第一个元素)
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
Optional<Integer> findFirst = integerAry.stream()
.findFirst();
if(findFirst.isPresent()){
System.out.println("findFirst: "+ findFirst.get());
}
reduce(结果归并)
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
归约是将集合中的所有元素经过指定运算,折叠成一个元素输出,如:求最值、平均数等,这些操作都是将一个集合的元素折叠成一个元素输出。
在流中,reduce函数能实现归约。reduce函数接收两个参数:
- 初始值
- 进行归约操作的Lambda表达式
元素求和:自定义Lambda表达式实现求和
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
//求和
Integer reduce = integerAry.stream()
.reduce(0, Integer::sum);
System.out.println("reduce::sum "+ reduce);
结果:
- reduce的第一个参数表示初试值为0;
- reduce的第二个参数为需要进行的归约操作,它接收一个拥有两个参数的Lambda表达式,reduce会把流中的元素两两输给Lambda表达式,最后将计算出累加之和。
count(计数)
count()
方法返回此流中元素的总数
long count();
List<Integer> integerAry = Arrays.asList(1, 2, 1, 2, 3, 6);
long count = integerAry.stream()
.count();
System.out.println("count: "+count);
结果:
中间操作和收集操作
操作 | 类型 | 返回类型 | 使用的类型/函数式接口 | 函数描述符 |
| 中间 |
|
|
|
| 中间 |
| ||
| 中间 |
| long | |
| 中间 |
|
|
|
| 中间 |
|
|
|
| 中间 |
| long | |
| 中间 |
|
|
|
| 终端 |
|
|
|
| 终端 |
|
|
|
| 终端 |
|
|
|
| 终端 |
| ||
| 终端 |
| ||
| 终端 |
|
|
|
| 终端 |
|
| |
| 终端 |
|
|
|
| 终端 |
|
Optional 干掉空指针
简介
- 空指针异常是导致Java应用程序失败的最常见原因。
- 为了解决空指针异常更加优雅,Java8 提供了
Optional
类库。 Optional
实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional
提供很多有用的方法,这样我们就不用显式进行空值检测。
代码示例
Optional.of()
或者Optional.ofNullable()
:创建Optional
对象,差别在于of
不允许参数是null
,而ofNullable
则无限制。
// 参数不能是null
Optional optional1 = Optional.of(1);
// 参数可以是null
Optional optional2 = Optional.ofNullable(null);
// 参数可以是非null
Optional optional3 = Optional.ofNullable(2);
Optional.empty()
:所有null包装成的Optional
对象
Optional optional1 = Optional.ofNullable(null);
Optional optional2 = Optional.ofNullable(null);
System.out.println(optional1 == optional2);// true
System.out.println(optional1 == Optional.empty());// true
Object o1 = Optional.empty();
Object o2 = Optional.empty();
System.out.println(o1 == o2);// true
isPresent()
:判断值是否存在
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
// isPresent判断值是否存在
System.out.println(optional1.isPresent() == true);
System.out.println(optional2.isPresent() == false);
ifPresent(Consumer consumer)
:如果option对象保存的值不是null,则调用consumer对象,否则不调用
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
// 如果不是null,调用Consumer
optional1.ifPresent(new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println("value is " + t);
}
});
// null,不调用Consumer
optional2.ifPresent(new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println("value is " + t);
}
});
orElse(value)
:如果optional对象保存的值不是null
,则返回原来的值,否则返回value
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
// orElse
System.out.println(optional1.orElse(1000) == 1);// true
System.out.println(optional2.orElse(1000) == 1000);// true
orElseGet(Supplier supplier)
:功能与orElse
一样,只不过orElseGet
参数是一个对象
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
System.out.println(optional1.orElseGet(() -> 1000) == 1);//true
System.out.println(optional2.orElseGet(() -> 1000) == 1000);//true
orElseThrow()
:值不存在则抛出异常,存在则什么不做,有点类似Guava
的Precoditions
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
optional1.orElseThrow(() -> {
throw new IllegalStateException();
});
try {
optional2.orElseThrow(() -> {
throw new IllegalStateException();
});
} catch (IllegalStateException e) {
e.printStackTrace();
}
filter(Predicate)
:判断Optional
对象中保存的值是否满足Predicate
,并返回新的Optional
。
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
Optional<Integer> filter1 = optional1.filter((a) -> a == null);
Optional<Integer> filter2 = optional1.filter((a) -> a == 1);
Optional<Integer> filter3 = optional2.filter((a) -> a == null);
System.out.println(filter1.isPresent());// false
System.out.println(filter2.isPresent());// true
System.out.println(filter2.get().intValue() == 1);// true
System.out.println(filter3.isPresent());// false
map(Function)
:对Optional
中保存的值进行函数运算,并返回新的Optional
(可以是任何类型)
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);
Optional<String> str1Optional = optional1.map((a) -> "key" + a);
Optional<String> str2Optional = optional2.map((a) -> "key" + a);
System.out.println(str1Optional.get());// key1
System.out.println(str2Optional.isPresent());// false
10.flatMap()
:功能与map()
相似,差别请看如下代码。flatMap
方法与map
方法类似,区别在于mapping
函数的返回值不同。map
方法的mapping
函数返回值可以是任何类型T
,而flatMap
方法的mapping
函数必须是Optional
。
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Optional<String>> str1Optional = optional1.map((a) -> Optional.of("key" + a));
Optional<String> str2Optional = optional1.flatMap((a) -> Optional.of("key" + a));
System.out.println(str1Optional.get().get());// key1
System.out.println(str2Optional.get());// key1
Optional类的方法
方法 | 描述 |
| 返回一个空的 Optional 实例 |
| 如果值存在并且满足提供的断言, 就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象 |
| 如果值存在,就对该值执行提供的 mapping 函数调用 |
| 如果值存在,就对该值执行提供的 mapping 函数调用,返回一个 Optional 类型的值,否则就返 回一个空的 Optional 对象 |
| 如果该值存在,将该值用 Optional 封装返回,否则抛出一个 NoSuchElementException 异常 |
| 如果值存在,就执行使用该值的方法调用,否则什么也不做 |
| 如果值存在就返回 true,否则返回 false |
| 将指定值用 Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException 异常 |
| 将指定值用 Optional 封装之后返回,如果该值为 null,则返回一个空的 Optional 对象 |
| 如果有值则将其返回,否则返回一个默认值 |
| 如果有值则将其返回,否则返回一个由指定的 Supplier 接口生成的值 |
| 如果有值则将其返回,否则抛出一个由指定的 Supplier 接口生成的异常 |