JDK 1.8 新特性之Stream

本文深入介绍了JDK 1.8中的Stream API新特性,包括从值、数组、集合、文件和函数生成流的方法,强调了中间操作和终端操作的概念,展示了如何进行数据筛选、映射、查找和归约操作。通过实例演示了流的创建、使用以及数值流的特化,揭示了内部迭代和流水线的优势,特别是如何在并行处理中提高效率。
摘要由CSDN通过智能技术生成

一、前言

流是 Java 8 的新成员,它允许你以声明式方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现)。此外,流还可以透明地并行处理,你无需写任何多线程代码了!

流的简短定义:从支持数据处理操作生成的元素序列

  • 元素序列:就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和访问元素(如ArrayList 与 LinkedList)。但流的目的在于表达计算。集合讲的是数据,流讲的是计算。
  • :流会使用一个提供数据的源,如集合、数组或输入/输出资源。 请注意,从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。
  • 数据处理操作:流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filtermapreducefindmatchsort等。流操作可以顺序执行,也可并行执行。

此外,流操作有两个重要的特点:

  • 流水线:很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。
  • 内部迭代:与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。

你可以往集合里加东西或者删东西,但是不管什么时候,集合中的每个元素都是放在内存里的。相比之下,流则是在概念上固定的数据结构(你不能添加或删除元素),其元素则是按需计算的。从另一个角度来说,流就像是一个延迟创建的集合:只有在消费者要求的时候才会计算值(用管理学的话说这就是需求驱动,甚至是实时制造)。与此相反,集合则是急切创建的(供应商驱动:先把仓库装满,再开始卖,就像那些昙花一现的圣诞新玩意儿一样)。

请注意,和迭代器类似,流只能遍历一次。遍历完之后,我们就说这个流已经被消费掉了。你可以从原始数据源那里再获得一个新的流来重新遍历一遍,就像迭代器一样(这里假设它是集合之类的可重复的源,如果是I/O通道就没戏了)。

java.util.stream.Stream中的Stream接口定义了许多操作,它们可以分为两大类:

  • 中间操作:诸如filtersorted等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查询。重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理 – 它们很懒。这是因为中间操作一般都可以合并起来,在终端操作时一次性全部处理。
  • 终端操作:终端操作会从流的流水线生成结果。其结果是任何不是流的值,比如ListInteger,甚至void

二、构建流

2.1 由值创建流

使用Stream的静态方法of,通过显式值创建一个流。

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

示例:

Stream<String> stream = Stream.of("A","B","C");
stream.map(String::toLowerCase).forEach(System.out::print);

2.2 由数组创建流

使用Arrays的静态方法stream可以从数组创建一个流。

public static <T> Stream<T> stream(T[] array)
public static <T> Stream<T> stream(T[] array,int startInclusive,int endExclusive)

注意:真多基本数据类型,都提供了对应的原始类型流特化的方法重载,如果是基本数据类型的的数组则会转换为对应的原始类型特化流,即IntStreamDoubleStreamLongStream

int[] numbers = {
  2, 3, 5, 7, 11, 13};
IntStream intStre= Arrays.stream(numbers);
int sum = intStre.sum();//总和是41

2.3 由集合创建流

Collection 接口增加了默认方法streamparallelStream()方法,因此任何集合的实现都可以调用这两个方法由集合创建流。区别在于,后者创建的是一个并行流。

default Stream<E> stream()
default Stream<E> parallelStream()

2.4 由文件生成流

Java 中用于处理文件等 I/O 操作的 NIO API(非阻塞 I/O)已更新,以便利用 Stream APIjava.nio.file.Files 中的很多静态方法都会返回一个流。

例如,一个很有用的方法是Files.lines,它会返回一个由指定文件中的各行构成的字符串流。使用你迄今所学的内容,你可以用这个方法看看一个文件中有多少各不相同的词:

long uniqueWords=0;
try(Stream<String> lines=Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
  //流会自动关闭
    uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))//生成单词流
            .distinct()//合并重复项
            .count();//统计各个不相同的单词的个数

}catch (IOException e) {
}

我们可以使用Files.lines得到一个流,其中的每个元素都是给定文件中的一行。然后,你可以对line调用split方法将行拆分成单词。应该注意的是,你该如何使用flatMap产生一个扁平的单词流,而不是给每一行生成一个单词流。最后,把distinctcount方法链接起来,数数流中有多少各不相同的单词。

2.5 由函数生成流:创建无限流

Stream API 提供了两个静态方法来从函数生成流: Stream.iterateStream.generate 。这两个操作可以创建所谓的无限流:不像从固定集合创建的流那样有固定大小的流。由iterategenerate产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去!一般来说,应该使用 limit(n) 来对这种流加以限制,以避免打印无穷多个值。

迭代

使用Stream的静态方法iterate,可以创建一个无限流。

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

示例:

Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);

iterate方法接受一个初始值(在这里是0),还有一个依次应用在每个产生的新值上的Lambda(UnaryOperator<t>类型)。这里,我们使用Lambda n -> n + 2,返回的是前一个元素加上2。因此, iterate方法生成了一个所有正偶

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值