Java8中Stream流操作的示例分析-创新互联

这篇文章给大家分享的是有关Java8中Stream流操作的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。

10年积累的网站设计制作、做网站经验,可以快速应对客户对网站的新想法和需求。提供各种问题对应的解决方案。让选择我们的客户得到更好、更有力的网络服务。我虽然不认识你,你也不认识我。但先网站制作后付款的网站建设流程,更有广安免费网站建设让你可以放心的选择与我们合作。

1 Stream流

对集合进行迭代时,可调用其iterator方法,返回一个iterator对象,之后便可以通过该iterator对象遍历集合中的元素,这被称为外部迭代(for循环本身正是封装了其的语法糖),其示意图如下:

Java8中Stream流操作的示例分析

除此之外,还有内部迭代方法,这正是这里要说明的集合的stream()方法返回的Stream对象的一系列操作,比如,要统计一个数字列表的偶数元素个数,当使用Stream对象的操作时,如下:

List list = new ArrayList(){{
    add(1);
    add(2);
    add(3);
}};

long count = list.stream().filter(num -> num % 2 == 0).count();
System.out.println(count);  // 1

其示意图如下:

Java8中Stream流操作的示例分析

上面提供的例子,比如filter,其参数为一个lambda表达式,所以Stream其实是用函数式编程方式在集合类上进行复杂操作的工具。

2 Stream流操作与Spark RDD算子

其实有Spark经验的人开始使用Stream流操作时,会有似曾相识的感觉,好像一切都那么熟悉。

下面从操作对象(名词)和对象操作(动词)两个角度来简单对比一下。

2.1 操作对象

Spark RDD算子的操作对象是RDD,中文意思是弹性分布式数据集,对用户而言,它就是类似集合一样的对象,里面存的是数据,只是底层它的数据可能分布于各个节点的各个partition,但不管怎样,其本质还是数据集。

Stream流操作的操作对象是集合,集合本质也是一种数据集,只是相比RDD,它是单机的。

2.2 对象操作

Spark RDD算子有两种类型,分别是Transformation算子和Action算子,前者是延迟计算的,它仅仅记住了数据的逻辑操作,并没有真正执行,后者是真正触发Transformation算子的计算。

Stream流操作也有两种类型,分别是惰性求值和及早求值(个人觉得这翻译不好),前者也只是记录了惰性求值的逻辑操作,后者才是真正触发操作。

可以看到其两者是非常相似的,一个是对分布式数据进行的各种操作,一个是单机数据进行的各种操作,把计算分为延迟计算和触发计算两种,好处是显而易见的:当对数据集进行多次逻辑操作时,有可能迭代只需要一次就可能完成,这样真正触发计算时,一次迭代带来的性能提升是显著的,比如对于过滤和计算这两个操作(前面计算偶数的操作),在一次迭代中就能够完成。

当然,不仅类型相似,其本身提供的操作的名称而言,都是相似的,有些东西真的是通用的。

3 常用Stream流操作

每个操作都用一个通俗易懂的例子来进行说明。

3.1 及早求值操作

3.1.1 collect(toList())

其作用是将Stream流中的元素收集起来,形成List、Set或Map等。

List list = Stream.of(1, 2, 3).collect(Collectors.toList());

System.out.println(list);   // [1, 2, 3]

1.Stream.of()方法用于方便地生成Stream流;

2.Collectors还有toSet()、toMap()等方法,详见其API。

3.1.2 forEach(Consumer)

对集合中的每个元素进行操作,其参数是Consumer函数接口。

Consumer printNum = System.out::print;
Stream.of(1, 2, 3).forEach(printNum);   // 123

System.out::print表示使用System.out类中的print方法,相当于lambda表达式:element -> System.out.print(element);

上面的例子也可以一步到位:

Stream.of(1, 2, 3).forEach(System.out::print);  // 123
3.1.3 max和min

其参数为Comparator对象,返回一个Optional对象,Optional说明其结果可能有,也可能没有(比如对空值的Stream流操作时)。

// 计算数值流中的大值
Optional maxOptional = Stream.of(1, 2, 3).max(Comparator.comparing(num -> num));
System.out.println(maxOptional.get());  // 3

// 找出字符串流中长度最小的字符串
Optional minOptional = Stream.of("a", "ab", "abc").min(Comparator.comparing(String::length));
System.out.println(minOptional.get());  // a

另外,其确实是及早求值操作,可以验证一下:

Stream.of(1, 2, 3).max(Comparator.comparing(num -> {
    System.out.println(num);
    return num;
}));

输出:

1
2
2
3

3.2 惰性求值操作

3.2.1 map

其参数为Function<T,R>,用于将Stream流中的值转换为另外一种流。

// 将字母转换为大写
Stream.of("a", "b", "hello")
    .map(String::toUpperCase)
    .forEach(element -> System.out.print(element + " "));  // A B HELLO
3.2.2 filter

其参数为Predicate<T>,过滤Stream流中的元素。

// 找出偶数
List list = Stream.of(1, 2, 3).filter(num -> num % 2 == 0).collect(Collectors.toList());

System.out.println(list);   // [2]
3.2.3 flatMap

其参数为Function<T,R>,只是此时R限定为Stream,将Stream流中的值转换为更多的流。

// 找出字符串中的单词
List list = Stream.of("hello you", "hello me")
    .flatMap(line -> Arrays.stream(line.split(" "))).collect(Collectors.toList());

System.out.println(list);   // [hello, you, hello, me]

是不是感觉跟Spark的wordcount例子有点像。

3.2.4 reduce

其参数为BinaryOperator<T>,返回一个Optional对象,Optional说明其结果可能有,也可能没有(比如对空值的Stream流操作时,并且没有指定初始值),用于归约操作。

// 求和
Integer res = Stream.of(1, 2, 3).reduce((acc, element) -> acc + element).get();

// 指定初始值6后,Stream的reduce操作结果肯定有值的,因此其返回的不是Optional,而直接是6所属的类型,即Integer
Integer res2 = Stream.of(1, 2, 3).reduce(6, (acc, element) -> acc + element);

System.out.println(String.format("res: %s, res2: %s", res, res2));  // res: 6, res2: 12

感谢各位的阅读!关于“Java8中Stream流操作的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

创新互联www.cdcxhl.cn,专业提供香港、美国云服务器,动态BGP最优骨干路由自动选择,持续稳定高效的网络助力业务部署。公司持有工信部办法的idc、isp许可证, 机房独有T级流量清洗系统配攻击溯源,准确进行流量调度,确保服务器高可用性。佳节活动现已开启,新人活动云服务器买多久送多久。


本文标题:Java8中Stream流操作的示例分析-创新互联
文章分享:http://scyanting.com/article/edssj.html