Java 8 数据流Stream的使用
一.Java里面对Stream的定义:
A sequence of elements supporting sequential and parallel aggregate operations.
支持顺序和集合并行操作的一系列元素。
(1)Stream是元素的集合,类似于Iterator,单向,不可往复,数据只能遍历一次,遍历过一次数据即用尽了
(2)Stream 可以并行化操作,迭代器只能命令式地、串行化操作;
(3)Stream 的另外一大特点是,数据源本身可以是无限的。
二.创建Stream
1.通过Stream接口的静态工厂方法:
(1)of方法;
(2) generate方法;
(3)iterate方法;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class NewStream {
public static void main(String[] args) {
//第一种 of方法;
int shuzu[] = {4, 5, 6, 7, 8};
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
integerStream.forEach(System.out::println);
Stream<String> stringStream = Stream.of("taobao", "weixin", "baidu", "toutiao");
stringStream.forEach(System.out::println);
Stream.of(shuzu).forEach(System.out::println);
//第二种 generate方法
Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return Math.random();
}
}).limit(10).forEach(System.out::println);
//第三种 iterate方法
Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println);
}
}
2.通过collection的子类来获取Stream(Collection接口有一个Stream方法,所以其所有子类都可以获取对应的Stream对象。)
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.stream().forEach(System.out::println);
LinkedList linkedList = new LinkedList();
linkedList.add(6);
linkedList.add(7);
linkedList.add(8);
linkedList.add(9);
linkedList.add(10);
//并行流
linkedList.parallelStream().forEach(System.out::println);
Set hashSet = new HashSet();
hashSet.add(11);
hashSet.add(12);
hashSet.stream().forEach(System.out::println);
三 .Stream操作
操作类型 |
接口方法 |
|
Intermediate (中间操作) |
无状态(Stateless) | map(mapToInt、mapToLong、flatmap、mapToDouble、 flatMapToInt、flatMapToLong、flatMapToDouble)、unordered、filter、 peek、 parallel 、sequential 、isParallel |
有状态(Stateful) | distinct、 sorted、limit、 skip | |
Terminal (最终操作) |
非短路操作 | forEach、forEachOrdered、 toArray、 reduce、collect、 max、min、count、 |
Short-circuiting (短路操作) |
anyMatch、allMatch、noneMatch、findFirst、 findAny |
中间操作和结束操作:
中间操作只是一种标记,结束操作才会触发实际计算;
中间操作后可以跟随其他操作,可以进行多次中间操作,结束操作执行后Stream元素就被消费掉了,无法对一个Stream进行两次terminal操作。
中间操作又可以分为无状态的(Stateless)和有状态的(Stateful),无状态的中间操作是指元素的处理不受前面元素的影响,而有状态的中间操作必须等到所有元素处理之后才知道最终结果,比如排序是有状态操作,在读取所有元素之前并不能确定排序结果,filter是无状态操作,符合过滤条件就被选出来,不关注其他元素的状态;
结束操作又可以分为短路操作和非短路操作,短路操作是指不用处理全部元素就可以返回结果,比如找到第一个满足条件的元素。
1.map()和flatMap()使用
map():将提供的函数应用于流的元素的结果
flatmap():将提供的流处理函数应用于流元素后获得的流元素
2.filter()使用
filter():根据传入的表达式,筛选出符合条件的元素
// 过滤不为null的元素 结果1 2 3 4 5 6 7 8 9
Stream.of(1, 2, 3, 4, 5, null, 6, null, 7, null, 8, 9).
filter(integerStreams -> integerStreams != null).forEach(System.out::println);
3.unordered()使用
4.forEach()和peek()使用
forEach()和peek()方法都可以接收一个Lambda表达式,然后在Stream的每个元素上执行该表达式。
不同的是:
forEach是terminal操作。peek是一个intermediate操作,可以多次操作Stream。
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ForeachAndPeek {
public static void main(String[] args) {
Stream a = Stream.of("one", "two", "three", "four");
/*
* forEach对于流来说是terminal操作,a第一次forEach后,就被"消费"掉了,所以第二次forEach就会报错
*/
a.forEach(System.out::println);
//a.forEach(System.out::println);
//Stream.of("one", "two", "three", "four").forEach(System.out::println).forEach(System.out::println);
//这是两个不同的流去执行forEach操作,所以不会报错
Stream.of("one", "two", "three", "four").forEach(System.out::println);
Stream.of("one", "two", "three", "four").forEach(p -> System.out.println(p));
/*
* peek操作不是terminal操作,而是intermediate操作。所以可以多次操作Stream
*/
Stream b = Stream.of("one", "two", "three", "four");
/*
* Stream b已经被操作或者被消费掉了,已经没有Stream b了,会报错。
* 报错信息:stream has already been operated upon or closed
*/
//b.peek(System.out::println);
//可以用一个新的Stream接收,再操作新的Stream
Stream c = b.peek(System.out::println).peek(p -> System.out.println(p));
c.peek(System.out::println);
/*
* peek操作的用法
*/
List streamList = Stream.of("one", "two", "three", "four").
filter(e -> e.length() > 3).peek(e -> System.out.println("Filtered value: " + e)).
map(String::toUpperCase).
peek(e -> System.out.println("Mapped value: " + e)).collect(Collectors.toList());
System.out.println(streamList);
}
}
5.sequential()和parallel()和isParallel()的使用:
sequential()是将并行流转化称串行流。parallel是将串行流转化成并行流。
isParallel判断是否为并行执行,输出结果为true和false。
List<Integer> list = new ArrayList<>();
for (int i=1;i<=10;i++){
list.add(i);
}
list.stream().parallel().forEach(a->System.out.print(a+","));
System.out.println();
list.parallelStream().sequential().forEach(a->System.out.print(a+","));
System.out.println();
System.out.println("===================");
System.out.println(list.stream().isParallel());
System.out.println(list.parallelStream().isParallel());
System.out.println(list.stream().parallel().isParallel());
System.out.println(list.parallelStream().sequential().isParallel());
输出结果为:
可以看出来,.parallel()输出的结果为乱序,.sequential() 方法输出的结果为顺序输出。
6.distinct()和count()使用
distinct():去重操作,输出结果为已经删除了重复元素的流元素;
count():返回流的元素个数;
public class distinct {
public static void main(String[] args) {
List list = new ArrayList();
list.add(1);
list.add("2");
list.add(3L);
list.add(1L);
list.add(4L);
list.add(3L);
list.stream().forEach((a)-> System.out.print(a+ " "));
System.out.println( "count:"+list.stream().count());
list.stream().distinct().forEach((a)-> System.out.print(a+ " "));
System.out.println( "count:"+list.stream().distinct().count());
}
}
输出结果如下:
7.sorted()使用
sorted():将数据按自然顺序进行排序。
sorted(Comparator<T>):将数据按提供的比较符进行排序。
8.limit()和skip()的使用
limit(long) 返回 Stream 的前面long个元素;
skip(long)丢弃了Stream的前面long个元素;
public class SkipAndLimit {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(5);
list.add(6);
list.add(3);
list.add(4);
list.add(7);
list.add(8);
list.add(9);
list.add(6);
list.add(3);
list.add(4);
list.stream().limit(3).forEach(System.out::print);
System.out.println();
System.out.println("~~~~~~~");
list.stream().skip(3).forEach(System.out::print);
}
}
输出结果集如下:
9.forEach()和forEachOrdered()使用
forEach是并行处理的;
forEachOrdered是按顺序处理的。
所以forEach效率会略高一点。
package Stream;
import java.util.Arrays;
import java.util.List;
public class ForEachAndForEachOrdered {
public static void main(String[] args) {
List<String> list = Arrays.asList("Two","Three","One","Four","Five");
list.stream().forEach(p->System.out.print(p+" "));
System.out.println("。");
list.stream().forEachOrdered(p->System.out.print(p+" "));
System.out.println("。");
//每次打印输出顺序是不一致的。
list.parallelStream().forEach(p->System.out.print(p+" "));
System.out.println("。");
list.parallelStream().forEachOrdered(p->System.out.print(p+" "));
System.out.println("。");
}
}
10.toArray()使用
11.reduce()使用
reduce:将流的元素聚合为一个汇总值。
public class Reduce {
public static void main(String[] args) {
// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println(concat);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println(minValue);
// 求和,sumValue = 1009, 有起始值(起始值+Sum(-1+1+2+3+4))
int sumValue = Stream.of(-1, 1, 2, 3, 4).reduce(1000, Integer::sum);
System.out.println(sumValue);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println(sumValue);
// 过滤,字符串连接,concat = "BDF"
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") < 0).reduce("", String::concat);
System.out.println(concat);
}
}
12.collect()使用
collect():将流的元素聚合到一个汇总结果容器中。
List<Integer> list = Stream.of(1,2,3,4,5,6,7,8,9).collect(Collectors.toList());
13.max()和min()使用
max()返回流中最大的值;
min()返回流中最小的值;
这几种方式都是可以的:
package Stream;
import java.util.Comparator;
import java.util.stream.Stream;
public class MaxAndMin {
public static void main(String[] args) {
int a = Stream.of(-99, -100, 3, 5, 9, 99).max(Integer::compare).get();//99
int b = Stream.of(-99, -100, 3, 5, 9, 99).min(Integer::compare).get();//-100
int c = Stream.of(-99, -100, 3, 5, 9, 99).max((n, m) -> Integer.compare(n, m)).get();//99
int d = Stream.of(-99, -100, 3, 5, 9, 99).min((n, m) -> Integer.compare(n, m)).get();//-100
int e = Stream.of(-99, -100, 3, 5, 9, 99).max(Comparator.comparing(Integer::valueOf)).get();//99
int f = Stream.of(-99, -100, 3, 5, 9, 99).min(Comparator.comparing(Integer::valueOf)).get();//-100
int j = Stream.of(-99, -100, 3, 5, 9, 99).mapToInt(i -> i).max().getAsInt();//99
int l = Stream.of(-99, -100, 3, 5, 9, 99).mapToInt(i -> i).min().getAsInt();//-100
System.out.println("a:"+a);
System.out.println("b:"+b);
System.out.println("c:"+c);
System.out.println("d:"+d);
System.out.println("e:"+e);
System.out.println("f:"+f);
System.out.println("j:"+j);
System.out.println("l:"+l);
}
}
输出结果如下:
还有一种经典的错误demo,这种方式是不可行的:
因为stream().max()是以返回值的正数,负数和0来判断大小的
public static void main(String[] args) {
//错误的用法
int g = Stream.of(-99,-100,3,5,9,99).max(Integer::max).get();
int h = Stream.of(-99,-100,3,5,9,99).min((v,k) -> {
int result = Integer.max(v, k);
return result;
}).get();
System.out.println("g:"+g);
System.out.println("h:"+h);
}
14.allMatch(),anyMatch()和noneMatch()的使用
allMatch:Stream中全部元素符合传入的条件返回true;
anyMatch:Stream中有一个元素符合传入的条件就返回true;
noneMatch:Stream中没有一个元素符合传入的条件返回true;
List<Integer> list = new ArrayList<>();
for (int i=1;i<=10;i++){
list.add(i);
}
list.stream().forEach(System.out::println);
System.out.println("全部大于9:"+list.stream().allMatch(a->a>9));
System.out.println("全部大于0:"+list.stream().allMatch(a->a>0));
System.out.println("有一个及以上大于9:"+list.stream().anyMatch(a->a>9));
System.out.println("有一个及以上大于10:"+list.stream().anyMatch(a->a>10));
System.out.println("没有一个大于9:"+list.stream().noneMatch(a->a>9));
System.out.println("没有一个大于10:"+list.stream().noneMatch(a->a>10));
输出结果为:
15.findFirst()和findAny()的使用
findFirst()返回第一个元素,如果流为空,则返回一个空Optional。
findAny()返回任意一个元素,如果流为空,则返回一个空Optional。对于并行流来说可能多次返回结果不一致,但是性能会略优秀于findFirst()。
public class FindFirstAndDFindAny {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();
for (int i = 1; i <= 10000; i++) {
list.add(i);
}
System.out.println(list.parallelStream().findAny());
System.out.println(list.parallelStream().findAny());
System.out.println(list.parallelStream().findAny());
System.out.println(list.parallelStream().findAny());
System.out.println(list.parallelStream().findAny());
System.out.println(list.parallelStream().findAny());
System.out.println(list.parallelStream().findAny());
System.out.println(list.parallelStream().findAny());
System.out.println(list.parallelStream().findFirst());
System.out.println(list2.parallelStream().findFirst());
}
}
结果如下:
可以看出多次执行并行流的findAny操作,结果是不一样的。