原文:https://mp.weixin.qq.com/s/N2zor5RzuHHTHQLHWVBttg

作者:思海同学  好好学java

其他资料:https://mp.weixin.qq.com/s/bSref5GwAxNbVVpSWI-66w

     https://mp.weixin.qq.com/s/2qj65eTzjUTjCEqlRahnZA

  相对于Java8之前的Java的相关操作简直是天差地别,Java8 的流式操作的出现,也很大程度上改变了开发者对于Java的繁琐的操作的印象,从此,Java也走向了函数式编程的道路!我相信只要长期的使用,一定可以精简你的代码,写的更加顺手,你也会喜欢上它的!

1 流的创建

1.1  创建流的方法

  既然需要聊聊流的操作,那么,首先还是先看看怎么创建流。创建流的方法有三种,分别是:Stream.of()、Stream.iterate()、Stream.generate(),然后,分别看一下这三个方法的声明。

static <T> Stream<T> of(T... values)

static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)

static <T> Stream<T> generate(Supplier<T> s)

  Stream.of():参数很简单,就是一系列的泛型参数。

  Stream.iterate():第一个参数是一个初始值,第二个参数是一个操作。

  Stream.generate():参数就是一个Supplier的供给型的参数。

1.2  创建流的方法举例

/**
* 创建Stream
*/
public static void createStream(){
//利用Stream.of方法创建流
Stream<String> stream = Stream.of("hello", "world", "Java8");
stream.forEach(System.out::println);
System.out.println("##################");
//利用Stream.iterate方法创建流,limit限制Stream的长度
List<Integer> stream2 = Stream.iterate(10, n -> n + 1).limit(5).collect(Collectors.toList());
stream2.forEach(System.out::println);
System.out.println("##################"); //利用Stream.generate方法创建流
List<Double> stream3 = Stream.generate(Math::random).limit(5).collect(Collectors.toList());
stream3.forEach(System.out::println);
System.out.println("##################"); //从现有的集合中创建流
List<String> strings = Arrays.asList("hello", "world", "Java8");
String string = strings.stream().collect(Collectors.joining(","));
System.out.println(string);
}

  在上面的例子中,Stream.of()方法的参数是几个字符串;Stream.iterate()方法的第一个参数是初始值 10,第二个参数是在10 的基础上每次加 1 的操作;Stream.generate()的参数是用 Random 方法产生随机数。

1.3 流的创建总结

  流的创建有三种方法,分别是Stream.of()、Stream.iterate()、Stream.generate(),这几个都是 Stream 类的静态方法,所以,使用起来非常的方便。

2. 流的操作

  我们知道怎么创建流了,接下来,我们就看看对流可以进行哪些操作,使用了 Stream 流之后,是否会比 Java8 之前方便很多呢?

2.1  装箱流

  在处理对象流的时候,可以利用 Collectors 类的静态方法转换为集合,例如,将字符串流转换为 List<String> ,这种方式是没有问题的。但是,如果遇到 double流想要转换为 List时,这是就会报错。

DoubleStream.of(1.0, 2.0, 3.0).collect(Collectors.toList());//错误的写法

  这种方式就是错误的,编译是不能通过的。别慌,对于这种问题,有 3 种比较好的解决方法。

  boxed 方法

  利用 boxed 方法,可以将 DoubleStream 转换为 Stream<Double> ,这样就解决了上面的问题,例如:

DoubleStream.of(1.0, 2.0, 3.0).boxed().collect(Collectors.toList());

  mapToObj 方法

  利用 mapToObj 方法也可以实现上面的功能,另外,也提供了 mapToInt、mapToLong、mapToDouble 等方法将基本类型流转换为相关包装类型。

DoubleStream.of(1.0, 2.0, 3.0).mapToObj(Double::valueOf).collect(Collectors.toList());

  collect 方法

  一般情况下,我们利用 collect 方法的时候,都是用于将流的数据收集为基本类型的集合,例如:

stream.collect(Collectors.toList())

  然而,collect 方法其实还有一种更加一般化的形式,如下:

<R> R collect(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiCnsumer<R,R> combiner)

  上面这种方法的第一个参数是一个供给器,相当于初始化一个容器,第二个参数是累加器,相当于给初始化的容器赋值,第三个参数是组合器,相当于将这些元素全部组合到一个容器。

  下面,我们通过一个简单的例子来看看到底是怎么使用的

List<Double> list = DoubleStream.of(1.0, 2.0, 3.0).collect(ArrayList<Double>::new, ArrayList::add, ArrayList::addAll);

  上面的例子我们可以看到,第一个参数:使用一个静态方法初始化一个 List 容器,第二个参数:使用静态方法 add ,添加元素,第三个参数:使用静态方法 addAll ,用于联合所有的元素。

  从最后的返回值为 List<Double>,我们也可以看出,全部组合成一个初始化的 List 集合中了。

2.2 字符串与流之间的转换

  这一小节主要讲解一下字符串与流之间的转换,将 String 转为流有两种方法,分别是 java.lang.CharSequence 接口定义的默认方法 chars 和 codePoints ,而将流转为字符串就是我们前面已经讲解到的方法 collect 。

public void testString2Stream() {
//使用codePoints把字符串转成Stream,在使用collect转成字符串
String s = "hello world Java8".codePoints()
.collect(StringBuffer::new, StringBuffer::appendCodePoint, StringBuffer::append).toString();
//使用chars把字符串转成Stream,在使用collect转成字符串
String s1 = "hello world Java8".chars()
.collect(StringBuffer::new, StringBuffer::appendCodePoint, StringBuffer::append).toString();
}

  在上面的例子中,先用chars 和 codePoints 方法转换为流,然后都是利用 collect 方法再转回字符串。

2.3 流的映射 map 与 flatMap

  流的映射是什么意思呢,我们先将一个在 Java8 之前的例子,我们常常需要将一个集合的对象的某一个字段取出来,然后再存到另外一个集合中,这种场景我们在 Java8 之前我们会这样实现。

public void mapTest() {
List<Person> list = new ArrayList<>();
list.add(new Person("Java5"));
list.add(new Person("Java6"));
list.add(new Person("Java7")); List<String> strings = new ArrayList<>();
for (Person p : list) {
strings.add(p.getName());
}
}

  是不是这样很麻烦,这也就是以前大家一直所说的 Python 用一招,Java 需要用花招!

  但是,Java8 却改变了这种现实,我们来看一看怎么使用 map 和 flatMap。

  首先,我们先看一下这俩个方法的声明;

<R> Stream<R> map(Function<? super T,? extends R> mapper)

<R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)

  接下来,我们用这两个方法改写上面的方式,先看看 map 方法;

public void mapTest() {
List<Person> list = new ArrayList<>();
list.add(new Person("Java5"));
list.add(new Person("Java6"));
list.add(new Person("Java7")); List<String> strings = list.stream().map(Person::getName).collect(Collectors.toList());
}

  通过使用 map 方法,参数给定 Person::getName 映射出 name,然后再用 collect 收集到 List 中,就完成了上面的负责的操作,是不是很舒服。

  但是,如果我们用 map 方法想要映射出集合属性,会遇到一个问题;

List<List<Friend>> collect = list.stream().map(Person::getFriends).collect(Collectors.toList());

  我们发现,上面的返回值是 List<List<Friend>>,这种形式集合里面还包着集合,处理有点麻烦,但是,不是还有另外 flatMap 没有使用吗,这个方法正好能够解决这个问题

List<Friend> collect1 = list.stream().flatMap(friend -> friend.getFriends().stream()).collect(Collectors.toList());

  发现,这个方法的返回值是 List<Friend>,正如我们看到的,flatMap 的方法能够“展平”包裹的流,这就是 map 和 flatMap 的区别。

  mapping函数,也是映射字段。

public static void main(String[] args) {
ArrayList<OrderInfo> orderArrayList = Lists.newArrayList();
orderArrayList.add(new OrderInfo("order111", "AAAA"));
orderArrayList.add(new OrderInfo("order111", "BBBB"));
orderArrayList.add(new OrderInfo("order222", "cccc"));
orderArrayList.add(new OrderInfo("order222", "DDDD")); Map<String, List<String>> collect = orderArrayList.stream().collect(Collectors.groupingBy(OrderInfo::getOrderNo,
Collectors.mapping(OrderInfo::getGoodsName, Collectors.toList())));
System.out.println(" ===" + JSONUtil.toJson(collect));
}

  上面的例子中,OrderInfo对象有两个字段,orderNo,goodsName,要根据orderNo对结果进行分组,goodsName组装成List,最终结果返回格式是Map<String, List<String>>

2.4 流的连接

  流的连接有两种方式,如果是两个流的连接,使用 Stream.concat 方法,如果是三个及三个以上的流的连接,就使用 Stream.flatMap 方法

public void testConcatStream() {
//两个流的连接
Stream<String> first = Stream.of("sihai", "sihai2", "sihai3");
Stream<String> second = Stream.of("sihai4", "sihai5", "sihai6");
Stream<String> third = Stream.of("siha7", "sihai8", "sihai9");
Stream<String> concat = Stream.concat(first, second);
//多个流的连接
Stream<String> stringStream = Stream.of(first, second, third).flatMap(Function.identity());
}

3 流的规约操作

  流的规约操作几种类型,这里都讲一下。

  内置的规约操作

  基本类型流都有内置的规约操作。包括average、count、max、min、sum、summaryStatistics,前面的几个方法相信不用说了,summaryStatistics 方法是前面的几个方法的结合,下面我们看看他们如何使用。

public void testReduce1() {
String[] strings = {"hello", "sihai", "hello", "Java8"};
long count = Arrays.stream(strings).map(String::length).count();
System.out.println(count);
System.out.println("##################"); int sum = Arrays.stream(strings).mapToInt(String::length).sum();
System.out.println(sum);
System.out.println("##################"); OptionalDouble average = Arrays.stream(strings).mapToInt(String::length).average();
System.out.println(average);
System.out.println("##################"); OptionalInt max = Arrays.stream(strings).mapToInt(String::length).max();
System.out.println(max);
System.out.println("##################"); OptionalInt min = Arrays.stream(strings).mapToInt(String::length).min();
System.out.println(min); DoubleSummaryStatistics statistics = DoubleStream.generate(Math::random).limit(1000).summaryStatistics();
System.out.println(statistics);
}

  基本的规约操作

  基本的规约操作是利用前面讲过的 reduce 方法实现的,IntStream 接口定义了三种 reduce 方法的重载形式,如下;

OptionalInt reduce(IntBinaryOperator op)

int reduce(int identity, IntBianryOperator op)

<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BianryOperator<U> combiner)

  上面的 identity 参数就是初始化值的意思,IntBianryOperator 类型的参数就是操作,例如 lambda 表达式;BianryOperator<U> combiner是一个组合器,在前面有讲过。

  下面我们通过一个例子来讲解一下。

public void testReduce2() {
int sum = IntStream.range(1, 20).reduce((x, y) -> x + y).orElse(0);
System.out.println(sum);
System.out.println("##################"); int sum2 = IntStream.range(1, 20).reduce(0, (x, y) -> x + 2 * y);
System.out.println(sum2);
System.out.println("##################");

int sum3 = IntStream.range(1, 20).reduce(0, Integer::sum);
System.out.println(sum3);
}

  例子中的第一个是1到20累加的操作,第二个以0为初始值,然后2倍累加,第三个是以0为初始值,累加。

  

  流的计数

  流的数量统计有两种方法,分别是 Stream.count() 方法和 Collectors.counting() 方法。

public void testStatistics() {
//统计数量
String[] strings = {"hello", "sihai", "hello", "Java8"};
long count = Arrays.stream(strings).count();
System.out.println(count);
System.out.println("##################"); Long count2 = Arrays.stream(strings).collect(Collectors.counting());
System.out.println(count2);
}

  

  流的查找

  流的查找 Stream 接口提供了两个方法 findFirst 和 findAny。findFirst 方法返回流中的第一个元素的 Optional,而 findAny 方法返回流中的某个元素的 Optional。

  我们来看一个例子。

String[] strings = {"hello", "sihai", "hello", "Java8"};
Optional<String> first = Arrays.stream(strings).findFirst();
System.out.println(first.get());
System.out.println("##################"); Optional<String> any = Arrays.stream(strings).findAny();
System.out.println(any.get());

  流的匹配

  流的匹配 Stream 接口提供了三个方法,分别是 anyMatch(任何一个元素匹配,返回 true)、allMatch(所有元素匹配,返回 true)、noneMatch(没有一个元素匹配,返回 true)。

boolean b = Stream.of(1, 2, 3, 4, 5, 10).anyMatch(x -> x > 5);
System.out.println(b);
System.out.println("##################"); boolean b2 = Stream.of(1, 2, 3, 4, 5, 10).allMatch(x -> x > 5);
System.out.println(b2);
System.out.println("##################"); boolean b3 = Stream.of(1, 2, 3, 4, 5, 10).noneMatch(x -> x > 5);
System.out.println(b3);

JDK8的Stream操作的更多相关文章

  1. JDK8中Stream使用解析

    JDK8中Stream使用解析 现在谈及JDK8的新特新,已经说不上新了.本篇介绍的就是Stream和Lambda,说的Stream可不是JDK中的IO流,这里的Stream指的是处理集合的抽象概念『 ...

  2. c#常见stream操作

    原文: c#常见stream操作 常见并常用的stream一共有 文件流(FileStream), 内存流(MemoryStream), 压缩流(GZipStream), 加密流(CrypToStre ...

  3. java8 Stream操作

    Stream操作详解:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/#icomments

  4. jdk8系列三、jdk8之stream原理及流创建、排序、转换等处理

    一.为什么需要 Stream Stream 作为 Java 8 的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念.它也不同于 StAX ...

  5. 看不懂JDK8的流操作?5分钟带你入门(转)

    在JDK1.8里有两个非常高级的新操作,它们分别是:Lambda 表达式和 Stream 流. Lambda表达式 让我们先说说 Lambda 表达式吧,这个表达式最大的作用就是简化语法,让代码更加易 ...

  6. Jdk8中Stream流的使用,让你脱离for循环

    学习要求: 知道一点儿函数式接口和Lambda表达式的基础知识,有利于更好的学习. 1.先体验一下Stream的好处 需求:给你一个ArrayList用来保存学生的成绩,让你打印出其中大于60的成绩. ...

  7. IntelliJ IDEA中如何优雅的调试Java Stream操作

    Stream操作是Java 8推出的一大亮点!虽然java.util.stream很强大,但依然还是有很多开发者在实际工作中很少使用,其中吐槽最多的一个原因就是不好调试,一开始确实是这样,因为stre ...

  8. jdk8的stream流式计算的操作

    jdk8之后增加了流式计算,现在根据学习了流式计算,并把过程记录下来: Person.java的bean package com.zhang.collectordemo; /** * @program ...

  9. JDK8 Stream操作整理

    1,forEach this.quoteItemList.forEach(p -> p.setMode(mode)); 2,获取对话属性,去重后生成集合 List<String> p ...

随机推荐

  1. CentOS7.7 安装并配置JDK 1.8

    本文介绍如何在CentOS中安装oracleJDK1.8并配置环境变量 1.下载并安装jdk1.8 进入下载页:https://www.oracle.com/technetwork/java/java ...

  2. Jenkins+tomcat自动发布的热部署/重启及遇到的坑解决办法

    一.背景 公司的项目一直手动maven打包.上传服务器.关闭/开启tomcat,整个流程下来耗时耗力,虽然可以将所有流程通过shell脚本一次性解决,但如果可以通过idea的Jenkins插件一键自动 ...

  3. 解决Linux搜狗输入法工具栏无法移动

    问题的出现 前两天一位朋友(@午后下午茶)发现一个有趣的情况: 为了复现bug,我把自己的输入法工具栏也挪到了顶栏,果然也无法挪动了 解决方法 原理不明,但解决方法很简单. 如图所示,随便找个输入框打 ...

  4. input type=file过滤图片

    <input type="file" accept=".png,.jpg,.jpeg,image/png,image/jpg,image/jpeg"> ...

  5. java 基本语法(十八)Lambda (五)Stream API

    1.Stream API的理解:1.1 Stream关注的是对数据的运算,与CPU打交道集合关注的是数据的存储,与内存打交道 1.2 java8提供了一套api,使用这套api可以对内存中的数据进行过 ...

  6. 数据可视化之powerBI入门(九)PowerBI数据建模:其实一点都不高深

    https://zhuanlan.zhihu.com/p/64149834 数据建模并没有那么高深,你同样可以学会!这篇文章通过一个实例创建一个简单的数据建模,并引出两个重要的概念:度量值和DAX. ...

  7. Flask 基础组件(六):Session

    除请求对象之外,还有一个 session 对象.它允许你在不同请求间存储特定用户的信息.它是在 Cookies 的基础上实现的,并且对 Cookies 进行密钥签名要使用会话,你需要设置一个密钥. 设 ...

  8. 阻止 iPhone 视频自动全屏

    最近一年都在做直播,遭video 全屏的问题困扰了很久.下面将阻止 ios视频自动全屏的办法写出来.添加 playsinline 和 webkit-playsinline="true&quo ...

  9. Mysql数据库搭建集群---实现主从复制,读写分离

    参考博客:https://blog.csdn.net/xlgen157387/article/details/51331244 A.  准备:多台服务器,且都可以互相随意访问root用户,都可以随意进 ...

  10. linux $* 和$@例子

    参见ibm网站示例: https://www.ibm.com/developerworks/cn/linux/l-bash-parameters.html 示例: [ian@pinguino ~]$ ...