java8 stream详解和使用
java8 stream使用
介绍
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
什么是 Stream?
Stream(流)是一个来自数据源的元素队列并支持聚合操作
1.元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
2. 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
3. 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
Stream的操作符大体上分为两种:中间操作符和终止操作符
- 中间操作符
对于数据流来说,中间操作符在执行制定处理程序后,数据流依然可以传递给下一级的操作符。
中间操作符包含8种(排除了parallel,sequential,这两个操作并不涉及到对数据流的加工操作):
- map(mapToInt,mapToLong,mapToDouble) 转换操作符,把比如A->B,这里默认提供了转int,long,double的操作符。
- flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作比如把 int[]{2,3,4} 拍平 变成 2,3,4 也就是从原来的一个数据变成了3个数据,这里默认提供了拍平成int,long,double的操作符。
- limit 限流操作,比如数据流中有10个 我只要出前3个就可以使用。
- distint 去重操作,对重复元素去重,底层使用了equals方法。
- filter 过滤操作,把不想要的数据过滤。
- peek 挑出操作,如果想对数据进行某些操作,如:读取、编辑修改等。
- skip 跳过操作,跳过某些元素。
- sorted(unordered) 排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。
- . 终止操作符
数据经过中间加工操作,就轮到终止操作符上场了;终止操作符就是用来对数据进行收集或者消费的,数据到了终止操作这里就不会向下流动了,终止操作符只能使用一次。
- collect 收集操作,将所有数据收集起来,这个操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以说Stream 的核心在于Collectors。
- count 统计操作,统计最终的数据个数。
- findFirst、findAny 查找操作,查找第一个、查找任何一个 返回的类型为Optional。
- noneMatch、allMatch、anyMatch 匹配操作,数据流中是否存在符合条件的元素 返回值为bool 值。
- min、max 最值操作,需要自定义比较器,返回数据流中最大最小的值。
- reduce 规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce。
- forEach、forEachOrdered 遍历操作,这里就是对最终的数据进行消费了。
- toArray 数组操作,将数据流的元素转换成数组。
代码演示
forEach 循环
//对集合进行遍历
List<String> asList = Arrays.asList("you","don't","bird","me","!","I", "don't", "bird", "you","!");
// 方式一:JDK1.8之前的循环方式
for (String str : asList) {
log.info(str);
}
log.info("---------------------2--------------------------");
// 方式二:使用Stream的forEach方法
asList.stream().forEach(f->log.info(f));
log.info("---------------------3--------------------------");
// 方式二:使用Stream的forEach方法简写方式
asList.stream().forEach(System.out::println);
//注:其实在数据量小时推荐使用增强for循环,如果在大数据量(大数据分析)下可以使用新的循环
输出
you
don't
bird
me
!
I
don't
bird
you
!
---------------------2--------------------------
you
don't
bird
me
!
I
don't
bird
you
!
---------------------3--------------------------
you
don't
bird
me
!
I
don't
bird
you
!
filter 过滤
//过滤年龄大于6的用户
List<Map<String,Object>> maplist = new ArrayList<Map<String,Object>>();
for(int i=1;i<10;i++) {
Map<String,Object> map1 = new HashMap<>();
map1.put("name", "user"+i);
map1.put("age", 0+i);
map1.put("phone", "15624545487");
map1.put("type", i<50?1:0);
maplist.add(map1);
}
maplist.stream().filter(f->Integer.parseInt(f.get("age").toString())>6).forEach(System.out::println);
输出
{phone=15624545487, name=user7, type=1, age=7}
{phone=15624545487, name=user8, type=1, age=8}
{phone=15624545487, name=user9, type=1, age=9}
map 映射
//将集合中的每个字符串对象转换为大写连续输出
List<String> list = Arrays.asList("how", "are", "you","?", "how", "old", "are", "you", "?");
list.stream().map(f->f.toUpperCase()).forEach(System.out::printf);
输出
HOWAREYOU?HOWOLDAREYOU?
flatMap 转换
//合并a.b两个集合
List<Integer> a = Arrays.asList(1, 2, 3);
List<Integer> b = Arrays.asList(4, 5, 6);
//将a,b集合添加到一个大集合中 组成二维数组集合
List<List<Integer>> collect = Stream.of(a, b).collect(Collectors.toList());
log.info(collect.toString());
// 将多个集合中的元素合并成一个集合
List<Integer> collect2 = Stream.of(a,b).flatMap(f->f.stream()).collect(Collectors.toList());
log.info(collect2.toString());
输出
[[1, 2, 3], [4, 5, 6]]
[1, 2, 3, 4, 5, 6]
sorted 排序
List<Integer> a = Arrays.asList(1, 2, 3);
a.stream().sorted((f1,f2)->f2.compareTo(f1)).collect(Collectors.toList()).forEach(System.out::print);
输出
321
distinct 去重复
Stream<String> stream = Stream.of("know", "is", "know", "noknow", "is", "noknow");
stream.distinct().forEach(f->log.info(f));
输出
know
is
noknow
count 总数量
Stream<String> stream = Stream.of("know", "is", "know", "noknow", "is", "noknow");
long count = stream.distinct().count();
log.info(count+"");
输出
3
min、max
List<String> list = Arrays.asList("1", "2", "3", "4", "5");
Optional<String> min = list.stream().min((x,y)->x.compareTo(y));
String minv = min.get();
log.info(minv);
Optional<String> max = list.stream().max((x,y)->x.compareTo(y));
String maxv = max.get();
log.info(maxv);
输出
1
5
skip、limit
List<String> list = Arrays.asList("a1", "b2", "c3", "d4", "e5");
//截取数组 2为数组的第二个以后开始(不包含2)
list.stream().skip(2).forEach(System.out::println);
//截取数组 2为数组的第2个截止(包含2)
list.stream().limit(2).forEach(System.out::println);
输出
c3
d4
e5
a1
b2
collect 收集
List<String> list = Arrays.asList("a", "b", "c", "d", "e");
List<String> list2 = list.stream().collect(Collectors.toList());
list2.add("f");
log.info(list2.toString());
Object[] array = list2.stream().toArray();
log.info(array.toString());
输出
[a, b, c, d, e, f]
[Ljava.lang.Object;@433d61fb
concat 合并
List<String> list = Arrays.asList("a", "b");
List<String> list2 = Arrays.asList("c", "d");
Stream.concat(list.stream(), list2.stream()).forEach(System.out::println);
Stream.of(list,list2).flatMap(f->f.stream()).forEach(System.out::println);
输出
a
b
c
d
a
b
c
d
anyMatch、allMatch 任意匹配 全匹配
List<String> list = Arrays.asList("you", "give", "me", "stop");
boolean anyMatch = list.stream().anyMatch(f->f.equals("me"));
log.info("{}",anyMatch);
boolean allMatch = list.stream().allMatch(f->f.equals("me"));
log.info("{}",allMatch);
输出
true
false
reduce 归纳
List<String> list = Arrays.asList("you", "give", "me", "stop");
String str = list.stream().reduce((before,after)->before + "," + after).get();
log.info(str);
输出
you,give,me,stop
findFirst、findAny 查找第一个和任意一个
Stream<String> stream = Stream.of("you", "give", "me", "stop");
// log.info(stream.findFirst().get());
log.info(stream.findAny().get());
输出
you
流转换成集合
List<Integer> list = Arrays.asList(1, 2, 3);
//[1, 2, 3]
List<Integer> collect = list.stream().map(f->f).collect(Collectors.toList());
log.info(collect.toString());
//[1, 2, 3]
Set<Integer> set = list.stream().map(f->f).collect(Collectors.toSet());
log.info(set.toString());
// {key1=value:1, key2=value:2, key3=value:3}
Map<String, String> map = list.stream().map(f->f).collect(Collectors.toMap(key->"key"+key, v->"value:"+v));
log.info(map.toString());
TreeSet<Integer> treeSet = Stream.of(3,4,2,1,5,4,7).collect(Collectors.toCollection(TreeSet::new));
log.info(treeSet.toString());
Map<String, String> map1 = list.stream().map(f->f).collect(Collectors.toMap(key->"key"+key, v->"value:"+v,(o1,o2)->o2,LinkedHashMap::new));
log.info(map1.toString());
输出
[1, 2, 3]
[1, 2, 3]
{key1=value:1, key2=value:2, key3=value:3}
[1, 2, 3, 4, 5, 7]
{key1=value:1, key2=value:2, key3=value:3}
joining 集合元素拼接
List<String> list2 = Arrays.asList("a", "b", "c");
String collect = list2.stream().collect(Collectors.joining());
log.info(collect);
// Collectors.joining(",")的结果是:a,b,c 然后再将结果 x + "d"操作, 最终返回a,b,cd
String str= Stream.of("a", "b", "c").collect(Collectors.collectingAndThen(Collectors.joining(","), x -> x + "d"));
log.info(str);
输出
abc
a,b,cd
元素聚合
// 求最大值 3
List<Integer> list = Arrays.asList(1, 2, 3);
Integer i = list.stream().collect(Collectors.collectingAndThen(Collectors.maxBy((a,b)->a-b), Optional::get));
log.info("{}",i);
Integer i1 = list.stream().collect(Collectors.maxBy((a,b)->a-b)).get();
log.info("{}",i1);
// 最小值 1
Integer y = list.stream().collect(Collectors.collectingAndThen(Collectors.minBy((a,b)->a-b), Optional::get));
log.info("{}",y);
Integer y1 = list.stream().collect(Collectors.minBy((a,b)->a-b)).get();
log.info("{}",y1);
//求和
Integer integer = list.stream().collect(Collectors.summingInt(f->f));
log.info("{}",integer);
// 平均值 2.0
Double double1 = list.stream().collect(Collectors.averagingDouble(f->f));
log.info("{}",double1);
String join = Stream.of("a","b","c").collect(Collectors.mapping(f->f.toString().toLowerCase(), Collectors.joining(",")));
log.info(join);
输出
3
3
1
1
6
2.0
a,b,c
groupingBy 分组
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 奇偶数分组:奇数分一组,偶数分一组
// 返回的结果是Map,其中key的数据类型为Function体中计算类型,value是List<T>类型,为分组的结果
Map<Boolean, List<Integer>> collect = list.stream().collect(Collectors.groupingBy(f->f%2==0));
//{false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]}
log.info(collect.toString());
// partitioningBy 用于分成两组的情况
Map<Boolean, List<Integer>> collect2 = list.stream().collect(Collectors.partitioningBy(f-> f%2 == 0));
//{false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]}
log.info(collect2.toString());
List<Map<String,Object>> maplist = new ArrayList<Map<String,Object>>();
for(int i=1;i<10;i++) {
Map<String,Object> map1 = new HashMap<>();
map1.put("name", "user"+i);
map1.put("age", 0+i);
map1.put("phone", "15624545487");
map1.put("type", i<5?1:0);
map1.put("money", 1.5+i);
maplist.add(map1);
}
// 根据某个字段进行分组
Map<Integer, List<Map<String, Object>>> collect3 = maplist.stream().collect(Collectors.groupingBy(f->Integer.parseInt(f.get("type").toString())));
//{
// 0=[{phone=15624545487, name=user5, type=0, age=5}, {phone=15624545487, name=user6, type=0, age=6}, {phone=15624545487, name=user7, type=0, age=7}, {phone=15624545487, name=user8, type=0, age=8}, {phone=15624545487, name=user9, type=0, age=9}],
// 1=[{phone=15624545487, name=user1, type=1, age=1}, {phone=15624545487, name=user2, type=1, age=2}, {phone=15624545487, name=user3, type=1, age=3}, {phone=15624545487, name=user4, type=1, age=4}]
//}
log.info(collect3.toString());
// 分组并对分组中的数据统计
Map<Integer, DoubleSummaryStatistics> collect4 = maplist.stream().collect(Collectors.groupingBy(f->Integer.parseInt(f.get("type").toString()),Collectors.summarizingDouble(f->Double.parseDouble(f.get("money").toString()))));
//{
// 0=DoubleSummaryStatistics{count=5, sum=42.500000, min=6.500000, average=8.500000, max=10.500000},
// 1=DoubleSummaryStatistics{count=4, sum=16.000000, min=2.500000, average=4.000000, max=5.500000}
//}
log.info(collect4.toString());
DoubleSummaryStatistics statistics1 = collect4.get(0);
DoubleSummaryStatistics statistics2 = collect4.get(1);
log.info("Sum:"+statistics1.getSum());
log.info("Average:"+statistics1.getAverage());
log.info("Max:"+statistics1.getMax());
log.info("Min:"+statistics1.getMin());
log.info("Count:"+statistics1.getCount());
log.info("--------------------------2---------------------------");
log.info("Sum:"+statistics2.getSum());
log.info("Average:"+statistics2.getAverage());
log.info("Max:"+statistics2.getMax());
log.info("Min:"+statistics2.getMin());
log.info("Count:"+statistics2.getCount());
输出
{false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]}
{false=[1, 3, 5, 7, 9], true=[2, 4, 6, 8, 10]}
{0=[{money=6.5, phone=15624545487, name=user5, type=0, age=5}, {money=7.5, phone=15624545487, name=user6, type=0, age=6}, {money=8.5, phone=15624545487, name=user7, type=0, age=7}, {money=9.5, phone=15624545487, name=user8, type=0, age=8}, {money=10.5, phone=15624545487, name=user9, type=0, age=9}], 1=[{money=2.5, phone=15624545487, name=user1, type=1, age=1}, {money=3.5, phone=15624545487, name=user2, type=1, age=2}, {money=4.5, phone=15624545487, name=user3, type=1, age=3}, {money=5.5, phone=15624545487, name=user4, type=1, age=4}]}
{0=DoubleSummaryStatistics{count=5, sum=42.500000, min=6.500000, average=8.500000, max=10.500000}, 1=DoubleSummaryStatistics{count=4, sum=16.000000, min=2.500000, average=4.000000, max=5.500000}}
Sum:42.5
Average:8.5
Max:10.5
Min:6.5
Count:5
--------------------------2---------------------------
Sum:16.0
Average:4.0
Max:5.5
Min:2.5
Count:4
reducing 累计操作
// sum: 是每次累计计算的结果,b是Function的结果
log.info(Stream.of(1, 3, 4).collect(
Collectors.reducing(0, x -> x, (sum, b) -> {
log.info(sum + " : " + b);
return sum + b;
}
)
).toString()
);
// 下面代码是对reducing函数功能实现的描述,用于理解reducing的功能
int sum = 0;
List<Integer> list3 = Arrays.asList(1, 3, 4);
for (Integer item : list3) {
int b = item;
log.info(sum + " : " + b);
sum = sum + b;
}
log.info(sum+"");
// 注意reducing可以用于更复杂的累计计算,加减乘除或者更复杂的操作
// result = 2 * 4 * 5 = 40
log.info(Stream.of(1, 3, 4).collect(Collectors.reducing(1, x -> x, (result, b) -> {
log.info(result + "-" + b);
return result * b;
})).toString());
输出
0 : 1
1 : 3
4 : 4
8
0 : 1
1 : 3
4 : 4
8
1-1
1-3
3-4
12