java8 Stream使用总结
【前言】 java8新特性
java8 函数接口
java8 Optional使用总结
Java 8 时间日期使用
java8 lambda表达式
1、流的介绍
Java8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。
而在Java 的集合 API 中,仅仅有极少量的辅助型方法,更多的时候是程序员需要用 Iterator 来遍历集合,完成相关的聚合应用逻辑。
所谓聚合操作就类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
2、流的特点
Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator,用户只要给出需要对其包含的元素执行什么操作,Stream 会隐式地在内部进行遍历,做出相应的数据转换,比如 “过滤掉长度大于 10 的字符串”。而原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作。
Stream 如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了。和迭代器不同的是,Stream 可以并行化操作,迭代器只能命令式地、串行化操作,Stream 的并行操作依赖于 Java7 中的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程。
3、流的操作类型
中间操作(intermediate )
一个流可以后面跟随零个或多个 intermediate 操作。其目的是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),仅仅调用到这类方法,并没有真正开始流的遍历。
对应方法有map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 skip、 parallel、 sequential、 unordered等
终止操作(terminal )
一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用尽了,无法再被操作,这是流的最后一个操作。
Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果或者一个错误。
对应方法有forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator
短路操作(short-circuiting)
对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。
对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。
对应方法有anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
4、流的生成
流的来源可以是集合,数组,I/O channel, 产生器generator 等。
基本数值型,目前有三种对应的包装类型 Stream:IntStream、LongStream、DoubleStream。
示例如下:
/*************流的来源*************/
// 1、of方法
// of(T... values):返回含有多个T元素的Stream
// of(T t):返回含有一个T元素的Stream
Stream<String> single = Stream.of("a");
Stream<String> multiple = Stream.of("a", "b", "c"); // 2、generator方法,返回一个无限长度的Stream,其元素由Supplier接口的提供。
Stream<String> generate = Stream.generate(() -> "a");
Stream<Double> generateA = Stream.generate(new Supplier<Double>() {
@Override
public Double get() {
return java.lang.Math.random();
}
});
// lambda写法
Stream<Double> generateB = Stream.generate(()-> java.lang.Math.random());
Stream<Double> generateC = Stream.generate(java.lang.Math::random); // 3、iterate方法,返回的也是一个无限长度的Stream,与generate方法不同的是,其是通过函数f迭代对给指定的元素种子而产生无限连续有序Stream,其中包含的元素可以认为是:seed,f(seed),f(f(seed))无限循环
Stream<Integer> iterate = Stream.iterate(1, item -> item + 2);
// 无限流处理
iterate.limit(10).forEach(System.out::println); // 4、empty方法返回一个空的顺序Stream,该Stream里面不包含元素项。
Stream<Object> empty = Stream.empty(); // 5、Collection接口和数组的默认方法
String chars[] = new String[]{"a", "b", "c"};
Arrays.stream(chars).forEach(System.out::println); List list = Arrays.asList(chars);
list.stream().forEach(System.out::println);
5、流的相关使用
当你熟悉使用Lambda表达式时,就可以很方便的进行流相关的操作了,提高代码的编写效率和程序的可读性。
/*************流的操作*************/
//concat 将两个Stream连接在一起,合成一个Stream。若两个输入的Stream都时排序的,则新Stream也是排序的;若输入的Stream中任何一个是并行的,则新的Stream也是并行的;若关闭新的Stream时,原两个输入的Stream都将执行关闭处理。
Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5))
.forEach(integer -> System.out.print(integer + " ")); //distinct 去除掉原Stream中重复的元素,生成的新Stream中没有没有重复的元素。
Stream.of(1,1,3,4,3).distinct().forEach(System.out::println); //filter 对原Stream按照指定条件过滤,过滤出满足条件的元素。
Stream.of(1,1,3,4,3).filter(x -> x > 2).forEach(System.out::println); //map方法将对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。
//为了提高处理效率,官方已封装好了,三种变形:mapToDouble,mapToInt,mapToLong,将原Stream中的数据类型,转换为double,int或者long类型。
Stream.of("a", "b", "c")
.map(item -> item.toUpperCase()) // .map(String::toUpperCase)
.forEach(System.out::println); // flatMap方法与map方法类似,都是将原Stream中的每一个元素通过转换函数转换,
// 不同的是,该换转函数的对象是一个Stream,也不会再创建一个新的Stream,而是将原Stream的元素取代为转换的Stream。
List<User> users = UserUtil.getUsers(6);
users.stream()
.flatMap(s -> s.getInterests().stream())
.forEach(System.out::println); Stream.of("a", "b", "c").flatMap(s -> Stream.of(s.toUpperCase())); //peek 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例)
Stream.of("a", "b", "c")
//优先执行
.peek(s -> System.out.println("peek:" + s))
.forEach(System.out::println); //skip 将过滤掉原Stream中的前N个元素,返回剩下的元素所组成的新Stream。
// 如果原Stream的元素个数大于N,将返回原Stream的后的元素所组成的新Stream;
// 如果原Stream的元素个数小于或等于N,将返回一个空Stream。
Stream.of("a", "b", "c").skip(2)
.forEach(System.out::println); // sorted方法将对原Stream进行排序,返回一个有序列的新Stream。sorterd有两种变体sorted(),sorted(Comparator),
// 前者将默认使用Object.equals(Object)进行排序,而后者接受一个自定义排序规则函数(Comparator),可自定义进行排序。
Stream.of(5, 6, 3, 9, 1)
.sorted()
.forEach(System.out::println); System.out.println("+++++++++++++++++++++++++++++++");
Stream.of(5, 6, 3, 9, 1)
.sorted(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//asc
return o1 - o2;
}
})
.forEach(System.out::println); Stream.of(5, 6, 3, 9, 1)
//desc
.sorted(((o1, o2) -> o2 - o1))
.forEach(System.out::println);
System.out.println("+++++++++++++++++++++++++++++++"); // count 将返回Stream中元素的个数。
long count = Stream.of(1, 2, 3, 4, 5).count();
System.out.println("count:" + count); // forEach 用于遍历Stream中的所元素,避免了使用for循环,让代码更简洁,逻辑更清晰。
Stream.of("a", "b", "c").forEach(System.out::println); // forEachOrdered 与forEach类似,都是遍历Stream中的所有元素,
// 不同的是,如果该Stream预先设定了顺序,会按照预先设定的顺序执行(Stream是无序的),默认为元素插入的顺序。
Stream.of(5,2,1,4,3)
.forEachOrdered(integer ->
System.out.println("integer:" + integer)
); // max 根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最大的元素。
Optional<Integer> max = Stream.of(5, 2, 2, 3, 4, 8)
.max((o1, o2) -> o2 - o1);
// Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5)
// .max(Comparator.comparingInt(x -> x));
Optional<Integer> max3 = Stream.of(1, 2, 3, 4, 5)
.max((o1, o2) -> o1 - o2);
int max2 = Stream.of(1, 2, 3, 4, 5)
.mapToInt(x -> x).max().getAsInt();
System.out.println("max = " + max.get() + " max2 = " + max2 + " max3 = " + max3.orElse(-1)); UserUtil.getUsers(6).stream()
.sorted(Comparator.comparing(User::getName).thenComparing(User::getId))
.forEach(u -> System.out.println(u.getName())); // min 根据指定的Comparator,返回一个Optional,该Optional中的value值就是Stream中最小的元素。
Optional<Integer> min = Stream.of(1, 2, 3, 4, 5)
.min((o1, o2) -> o1 - o2);
System.out.println("min:" + min.get()); System.out.println("*********************reduce********************");
// reduce
// 1、reduce((sum, item) -> { ... }); //返回Optional,因为可能存在为空的情况,
// 2、reduce(0, (sum, item) -> { ... }); /返回对应类型,不存在为空的情况
//无初始值,第一个参数为stream的第一个元素,第二个参数为stream的第二个元素,计算的结果赋值给下一轮计算的sum
Optional<Integer> optional = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) -> {
System.out.println("sum before:" + sum);
System.out.println("item:" + item);
sum = sum + item;
System.out.println("sum after:" + sum); return sum; // return Integer.sum(sum, item);
});
//等效
Optional<Integer> optional1 = Stream.of(1, 2, 3, 4, 5).reduce((sum, item) ->
Integer.sum(sum, item)
);
//等效
Optional<Integer> optional2 = Stream.of(1, 2, 3, 4, 5).reduce(Integer::sum);
System.out.println("integer = " + optional.orElse(-1));
System.out.println("*****************************************");
//给定初始值,第一个参数为初始值,第二个参数为stream的第一个元素,计算的结果赋值给下一轮计算的sum
Integer reduce = Stream.of(1, 2, 3, 4, 5).reduce(5, (sum, item) -> {
System.out.println("sum2 before:" + sum);
System.out.println("item:" + item);
sum = sum + item;
System.out.println("sum2 after:" + sum);
return sum;
});
//等效
Integer reduce2 = Stream.of(1, 2, 3, 4, 5).reduce(0, (sum, item) ->
Integer.sum(sum, item)
);
//等效
Integer reduce3 = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum);
System.out.println("reduce = " + reduce); System.out.println("*********************collect********************");
List<Integer> toList = Stream.of(1, 2, 3, 4)
.collect(Collectors.toList());
List<Integer> toList2 = Stream.of(1, 2, 3, 4)
.collect(Collectors.toCollection(ArrayList::new));
System.out.println("toList: " + toList); Set<Integer> toSet = Stream.of(1, 2, 3, 4)
.collect(Collectors.toSet());
Set<Integer> toSet2 = Stream.of(1, 2, 3, 4)
.collect(Collectors.toCollection(() -> new TreeSet()));
System.out.println("toSet: " + toSet); //(value1, value2) -> value1 用前面的value覆盖后面的value,保持不变
List<User> userList = UserUtil.getUsers(5);
userList.add(new User(2, "fjw"));
Map<Integer, String> toMap = userList.stream()
.collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value1));
System.out.println("(value1, value2) -> value1");
toMap.forEach((k, v) -> System.out.println(k + "-" + v)); // 对value值进行了限定不能为null,否则抛出空指针异常
// userList.add(new User(3, null));
//(value1, value2) -> value2 用后面的value覆盖前面的value
Map<Integer, String> toMap2 = userList.stream()
.collect(Collectors.toMap(User::getId, User::getName, (value1, value2) -> value2));
System.out.println("(value1, value2) -> value2");
toMap2.forEach((k, v) -> System.out.println(k + "-" + v)); // 解决value值为null方式
userList.add(new User(4, null));
Map<Integer, String> toMap3 = userList.stream()
.collect(HashMap::new, (m, u) -> m.put(u.getId(), u.getName()), HashMap::putAll);
toMap3.forEach((k, v) -> System.out.println(k + "-" + v)); Optional<Integer> maxBy = Stream.of(1, 2, 3, 4)
.collect(Collectors.maxBy(Comparator.comparingInt(o -> o)));
System.out.println("maxBy:" + maxBy.get()); Long counting = Stream.of(1, 2, 3, 4)
.collect(Collectors.counting());
System.out.println("counting:" +counting); //分割数据块
Map<Boolean, List<Integer>> partitioningBy = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.partitioningBy(item -> item > 3));
//partitioningBy : {false=[1, 2, 3], true=[4, 5]}
System.out.println("partitioningBy : " + partitioningBy); Map<Boolean, Long> collect = Stream.of(1, 2, 3, 4)
.collect(Collectors.partitioningBy(item -> item > 3, Collectors.counting()));
System.out.println("collect: " + collect); //数据分组
Map<Boolean, List<Integer>> groupingBy = Stream.of(1, 2, 3, 4, 5)
.collect(Collectors.groupingBy(item -> item > 3));
//partitioningBy : {false=[1, 2, 3], true=[4, 5]}
System.out.println("groupingBy : " + groupingBy); //字符串
String joining = Stream.of("a", "b", "c", "d")
.collect(Collectors.joining(","));
System.out.println(joining); String joining2 = Stream.of("a", "b", "c", "d")
.collect(Collectors.joining(",", "[", "]"));
System.out.println(joining2);
// 根据id分组
Map<Integer, List<User>> collect3 = userList.stream().collect(Collectors.groupingBy(User::getId));
// 将id映射为id集合
List<Integer> collect1 = userList.stream().collect(Collectors.mapping(User::getId, Collectors.toList()));
// 根据id分组后将interests映射成集合
Map<Integer, List<List<String>>> collect2 = userList.stream()
.collect(Collectors.groupingBy(User::getId, Collectors.mapping(User::getInterests, Collectors.toList())));
System.out.println(collect3);
System.out.println(collect1);
System.out.println(collect2);
源码参照Github
java8 Stream使用总结的更多相关文章
- java List递归排序,传统方式和java8 Stream优化递归,无序的列表按照父级关系进行排序(两种排序类型)
当有一个List列表是无序的,List中的数据有parentid进行关联,通过java排序成两种排序类型: 所用的测试列表最顶级无parentid,若为特殊值,修改下判断方法即可. 第一种排序:按照树 ...
- java8 Stream的实现原理 (从零开始实现一个stream流)
1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2 ...
- 简洁又快速地处理集合——Java8 Stream(下)
上一篇文章我讲解 Stream 流的基本原理,以及它与集合的区别关系,讲了那么多抽象的,本篇文章我们开始实战,讲解流的各个方法以及各种操作 没有看过上篇文章的可以先点击进去学习一下 简洁又快速地处理集 ...
- 简洁又快速地处理集合——Java8 Stream(上)
Java 8 发布至今也已经好几年过去,如今 Java 也已经向 11 迈去,但是 Java 8 作出的改变可以说是革命性的,影响足够深远,学习 Java 8 应该是 Java 开发者的必修课. 今天 ...
- Java8 Stream性能如何及评测工具推荐
作为技术人员,学习新知识是基本功课.有些知识是不得不学,有些知识是学了之后如虎添翼,Java8的Stream就是兼具两者的知识.不学看不懂,学了写起代码来如虎添翼. 在上篇<Java8 Stre ...
- Java8 Stream新特性详解及实战
Java8 Stream新特性详解及实战 背景介绍 在阅读Spring Boot源代码时,发现Java 8的新特性已经被广泛使用,如果再不学习Java8的新特性并灵活应用,你可能真的要out了.为此, ...
- 如何通过 IntelliJ IDEA 来提升 Java8 Stream 的编码效率
本文翻译整理自:https://winterbe.com/posts/2015/03/05/fixing-java-8-stream-gotchas-with-intellij-idea 作者:@Wi ...
- 如何用Java8 Stream API找到心仪的女朋友
传统的的Java 集合操作是有些啰嗦的,当我们需要对结合元素进行过滤,排序等操作的时候,通常需要写好几行代码以及定义临时变量. 而Java8 Stream API 可以极大简化这一操作,代码行数少,且 ...
- 【转】Java8 Stream 流详解
当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是 ...
- 何用Java8 Stream API进行数据抽取与收集
上一篇中我们通过一个实例看到了Java8 Stream API 相较于传统的的Java 集合操作的简洁与优势,本篇我们依然借助于一个实际的例子来看看Java8 Stream API 如何抽取及收集数据 ...
随机推荐
- JSP指令、标签以及中文乱码
JSP指令.标签以及中文乱码 一.JSP指令简介 JSP指令(directive)是为JSP引擎而设计的,它们并不直接产生任何可见输出,而只是告诉引擎如何处理JSP页面中的其余部分. JSP指令的基本 ...
- Hadoop3.1.2 + Hbase2.2.0 设置lzo压缩算法
Hadoop3.1.2 + Hbase2.2.0 设置lzo压缩算法: 写在前面,在配置hbase使用lzo算法时,在网上搜了很多文章,一般都是比较老的文章,一是版本低,二是一般都是使用hadoop- ...
- javaScript 基础知识汇总(六)
1.基本类型与对象的区别 基本类型:是原始类型的中的一种值. 在JavaScript中有6中基本类型:string number boolean symbol null undefined 对 ...
- Redis持久化的原理及优化
更多内容,欢迎关注微信公众号:全菜工程师小辉~ Redis提供了将数据定期自动持久化至硬盘的能力,包括RDB和AOF两种方案,两种方案分别有其长处和短板,可以配合起来同时运行,确保数据的稳定性. RD ...
- cogs 1377. [NOI2011] NOI嘉年华 (dp
题意:给你n个活动的起止时间,要你从中选一些活动在2个会场安排(不能有两个活动在两个会场同时进行),使活动较少的会场活动数最大,以及在某个活动必须选择的前提下,求该答案. 思路:由于n很小,时间很大, ...
- 牛客暑假多校第二场 F trade
题意: 白兔有n个仓库,每个仓库有啊ai个货物,在每个仓库白兔可以装上任意数量的货物,也可以卸下任意数量的货物,现在有k个圆形信号阻隔器,然后有m个顾客下个一个订单,每个顾客的收货量有一个上限, 在每 ...
- CodeChef - QRECT Rectangle Query CDQ分治
题目传送门 题解:现在需要维护的每次的询问矩形和前面插入的所有矩形有公共部分的个数. 我们试着直接去维护这个东西, 发现可能的情况太多,不好维护,所以我们维护每次询问的时候在当前矩阵个数下,有多少个矩 ...
- 详解RMQ-ST算法 ST模板
RMQ问题是求解区间最值的问题. 这里分析的是ST算法,它可以对所有要处理的数据做到O(nlogn)的预处理,对每个区间查询做到O(1)查询 ST算法本质是一个DP的过程 这里通过举一个求最大值实例来 ...
- essential C++中的一些疑问记录
关于书中P87下列代码中,less<int>的使用,我目前的理解是 less<int> 是一个类型,& it 是对外部参数的引用.但是为何要加上引用,另外 调用该函数时 ...
- js中的所有兼容问题总结
js兼容问题总结 在学习js过程中很多人都遇到过兼容问题,这些兼容问题是因为各版本浏览器不同导致的,为了解决这些兼容问题,js给我们提供了解决这些兼容问题的方案,对此,我个人进行了汇集以及总结. ...