【前言】 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使用总结的更多相关文章

  1. java List递归排序,传统方式和java8 Stream优化递归,无序的列表按照父级关系进行排序(两种排序类型)

    当有一个List列表是无序的,List中的数据有parentid进行关联,通过java排序成两种排序类型: 所用的测试列表最顶级无parentid,若为特殊值,修改下判断方法即可. 第一种排序:按照树 ...

  2. java8 Stream的实现原理 (从零开始实现一个stream流)

    1.Stream 流的介绍 1.1 java8 stream介绍 java8新增了stream流的特性,能够让用户以函数式的方式.更为简单的操纵集合等数据结构,并实现了用户无感知的并行计算. 1.2  ...

  3. 简洁又快速地处理集合——Java8 Stream(下)

    上一篇文章我讲解 Stream 流的基本原理,以及它与集合的区别关系,讲了那么多抽象的,本篇文章我们开始实战,讲解流的各个方法以及各种操作 没有看过上篇文章的可以先点击进去学习一下 简洁又快速地处理集 ...

  4. 简洁又快速地处理集合——Java8 Stream(上)

    Java 8 发布至今也已经好几年过去,如今 Java 也已经向 11 迈去,但是 Java 8 作出的改变可以说是革命性的,影响足够深远,学习 Java 8 应该是 Java 开发者的必修课. 今天 ...

  5. Java8 Stream性能如何及评测工具推荐

    作为技术人员,学习新知识是基本功课.有些知识是不得不学,有些知识是学了之后如虎添翼,Java8的Stream就是兼具两者的知识.不学看不懂,学了写起代码来如虎添翼. 在上篇<Java8 Stre ...

  6. Java8 Stream新特性详解及实战

    Java8 Stream新特性详解及实战 背景介绍 在阅读Spring Boot源代码时,发现Java 8的新特性已经被广泛使用,如果再不学习Java8的新特性并灵活应用,你可能真的要out了.为此, ...

  7. 如何通过 IntelliJ IDEA 来提升 Java8 Stream 的编码效率

    本文翻译整理自:https://winterbe.com/posts/2015/03/05/fixing-java-8-stream-gotchas-with-intellij-idea 作者:@Wi ...

  8. 如何用Java8 Stream API找到心仪的女朋友

    传统的的Java 集合操作是有些啰嗦的,当我们需要对结合元素进行过滤,排序等操作的时候,通常需要写好几行代码以及定义临时变量. 而Java8 Stream API 可以极大简化这一操作,代码行数少,且 ...

  9. 【转】Java8 Stream 流详解

      当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是 ...

  10. 何用Java8 Stream API进行数据抽取与收集

    上一篇中我们通过一个实例看到了Java8 Stream API 相较于传统的的Java 集合操作的简洁与优势,本篇我们依然借助于一个实际的例子来看看Java8 Stream API 如何抽取及收集数据 ...

随机推荐

  1. 分布式任务调度框架 Azkaban —— Flow 2.0 的使用

    一.Flow 2.0 简介 1.1 Flow 2.0 的产生 Azkaban 目前同时支持 Flow 1.0 和 Flow2.0 ,但是官方文档上更推荐使用 Flow 2.0,因为 Flow 1.0 ...

  2. Chrome 开发工具之 Application

    Chrome 开发者工具有 Application 这么一个面板,主要作用是检查 web 应用加载的所有资源,包括 Manifest.Service Workers.Local Storage.Ses ...

  3. 微信小程序获取手机号码看这篇文章就够了

    前言 微信小程序获取手机号码,从官方文档到其他博主的文档 零零散散的 (我就是这样看过来 没有一篇满意的 也许是我搜索姿势不对) 依旧是前人栽树 后人乘凉 系列.保证看完 就可以实现获取手机号码功能 ...

  4. hdu 4825 Xor Sum(01字典树模版题)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4825 题解:一到01字典树的模版题,01字典树就是就是将一些树用二进制放到一个树上这样可以方便对整体异 ...

  5. 图论之拓扑排序 poj1128 Frame Stacking

    题目网址 http://poj.org/problem?id=1128 思路:遍历找出每一种字母出现的最大和最小的横纵坐标,假如本应出现字母A的地方出现了字母B,那么A一定在字母B之前,这就相当于点A ...

  6. PAT L3-015. 球队“食物链”

    L3-015. 球队“食物链” 时间限制 1000 ms 内存限制 262144 kB 代码长度限制 8000 B 判题程序 Standard 作者 李文新(北京大学) 某国的足球联赛中有N支参赛球队 ...

  7. MPA JS CSS预处理方案

    1.WebPack 添加配置文件webpack.config.js,直接在当前目录运行 webpack. var basepath = '/root/webapps/happ'; var glob = ...

  8. python-积卷神经网络全面理解-tensorflow实现手写数字识别

    首先,关于神经网络,其实是一个结合很多知识点的一个算法,关于cnn(积卷神经网络)大家需要了解: 下面给出我之前总结的这两个知识点(基于吴恩达的机器学习) 代价函数: 代价函数 代价函数(Cost F ...

  9. 用PhpStrom线上连接修改linux服务器上代码配置

    为了进一步提高自己的技能水平,不久前入手了一台服务器,不贵,一年也就不到两百,因为自己对于linux机器比较生疏,命令用的有点抠脚.老需要查阅处理. 于是我选择用PhpStrom直接连接线上服务器,通 ...

  10. 用.NET写“算命”程序

    用.NET写"算命"程序 "算命",是一种迷信,我父亲那一辈却执迷不悟,有时深陷其中,有时为求一"上上签",甚至不惜重金,向"天神 ...