千锋教育-做有情怀、有良心、有品质的职业教育机构

400-811-9990
手机站
千锋教育

千锋学习站 | 随时随地免费学

千锋教育

扫一扫进入千锋手机站

领取全套视频
千锋教育

关注千锋学习站小程序
随时随地免费学习课程

上海
  • 北京
  • 郑州
  • 武汉
  • 成都
  • 西安
  • 沈阳
  • 广州
  • 南京
  • 深圳
  • 大连
  • 青岛
  • 杭州
  • 重庆
当前位置:重庆千锋IT培训  >  技术干货  >  Stream是什么,有什么?

Stream是什么,有什么?

来源:千锋教育
发布人:xqq
时间: 2023-10-14 21:59:45

一、Stream

Stream是什么

Java 8新增了一个API叫做Stream ,Stream的英文可以理解为流动的液体,其实这就是一个辅助处理集合数据的工具类,工具的更新必然带来的是生产力的提升,这里的生产力代表的就是整洁优雅的代码,更高的灵活度,更好的性能。相信各类的技术文章(包括博客和书籍)已经写过无数遍了。比如下面摘录《Java 8实战》关于流的描述:

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

这段话的表述个人感觉类似于抓手、赋能、心智之类的PPT黑话,看着挺高级的,也能懂一些,但也不是很懂,反正如果对于不知道Stream的人,并不能建立直接的理解。

所以流到底是什么呢?是一个接口。让我们看看它的声明:

public interface Stream extends BaseStream> {

  Stream filter(Predicate predicate);

  Stream map(Function mapper);

  void forEach(Consumer action);

  …

}

就是个接口,然后这个借口有一些抽象方法:filter,map,forEach等等。我们可以看到有些方法返回了新的Stream,有些直接是void。这个接口用来干什么用呢?处理集合数据。为什么这么说?看下面一个Collection接口的方法:

public interface Collection extends Iterable {

  …

  default Stream stream() {

        return StreamSupport.stream(spliterator(), false);

    }

}

那么所有继承了Collection的接口都可以直接创建Stream,然后再执行Stream里面的操作。所以这么看下来,首先得承认书中的表述是高度抽象且精炼的,这是书籍该做的事情。但从易于理解的角度,我觉得可以说是简洁高效安全的处理集合数据的工具类。如下图所示,Stream是一个中间过程。

需要注意的点

首先Stream不是一个数据结构,它不存储任何数据,它是一种数据处理工具,代表了一种能力。Stream不会对处理的数据本身做任何修改,永远都是返回新的Stream或者最终的处理结果。Stream可以有多个中间操作,但只能有一个终端操作,因为终端操作就求值了。一个Stream只能用一次,不能多次复用。(因为它不存储数据,只是一个转换能力)。

能力范围

Stream随着Java 8的发布已经8年多了,在我有限的职业生涯里,碰到的一些职场新人依然有些人觉得使用for或者iterator来遍历集合更易读易懂。但如果他真正了解Stream所蕴含的能力后,应该会转变想法。下面简单介绍一下Stream都提供了什么样的能力。

生成流java.util.stream.Stream#of(T… values) 。首先stream接口本身提供了一个静态默认方法,可以直接创建,这里的可变参数会被解析成一个数组。java.util.Collection#stream()java.util.Arrays#stream(T[] array)java.nio.file.Files#list(Path dir)java.nio.file.Files#lines(Path path)

可以看到,可以操作stream的对象基本为List或者Array

2. 筛选和切片

这可能是用的非常多的功能。对应的方法为:

filter:接受一个Predicate断言函数,用来遍历元素是否符合断言条件。可以简单的理解为一个过滤器。distinct:无参数,将所有元素去重,和数据库的distinct关键词能力一样。limit:接受一个int型长度字段,表示要保留多少个元素,需要注意的时候limit并不排序。skip:和limit相对应,接受一个int型长度字段表示跳过多少个元素,也不排序。

下面举个例子:

Stream.of(“d2”, “a2”, “b1”, “a3”, “c1”, “a4”, “a2”, “a1”)

        .filter(x -> x.startsWith(“a”))

        .distinct()

        .skip(1)

        .limit(3)

        .forEach(System.out::println);

  }

// output:

a3

a4

a1

3. 映射/转换

这里主要是map,map代表了一种对应关系,即地图坐标与实际地点的对应关系,我们有了经纬度就可以准确的找到地址,这个例子可以很形象的解释map命名的由来和功能。

map:接受一个Function作为参数,即输入一个值,返回另一个值,满足转换的语义。flatmap:同样接受一个Function作为参数,不同的是这个Function中有一个参数是一个stream,返回的也是一个stream,意为将多个stream连成一个stream。

同样,举个简单的例子:

Stream.of(“d2”, “a2”, “b1”, “a3”, “c1”, “a4”, “a2”, “a1”)

        .filter(x -> x.startsWith(“a”))

        .map(String::toUpperCase)

        .forEach(System.out::println);

//output

A2

A3

A4

A2

A1

List list = Stream.of(“Hello”, “world!”)

        .map(s -> s.split(“”))

        .flatMap(Arrays::stream)

        .collect(Collectors.toList());

System.out.println(list);

//output

[H, e, l, l, o, w, o, r, l, d, !]

4. 查找和匹配

这里的能力可以认为是一个加强版的contains方法,具备多种查找匹配能力。

allMatch:返回boolean,接受一个Predicate断言,确认全部元素均满足这个条件则返回true,否则返回falseanyMatch:与allMatch类似,但从语义上可以区分只要任意元素满足条件即可noneMatch:同样,要求没有任何元素满足条件findFirst:返回一个Optional,里面是满足条件的名列前茅个元素findAny:返回Optional,里面是满足条件的任一元素

这里需要解惑的是findAny与findFirst的区别,因为这两个都是找到满足条件的元素就返回,但findFirst会在限制并行流的计算,会严格按照集合中元素的顺序来依次查找。findAny就不会有这个限制。如果非并行计算场景,这二者并无区别。

下面依旧举简单的例子说明:

boolean b1 = Stream.of(“d2”, “a2”, “b1”, “a3”, “c1”, “a4”, “a2”, “a1”)

        .anyMatch(x -> x.startsWith(“a”));

    System.out.println(b1);

//output: true

String s2 = Stream.of(“d2”, “a2”, “b1”, “a3”, “c1”, “a4”, “a2”, “a1”)

        .filter(x -> x.startsWith(“a”))

        .findFirst()

        .get();

    System.out.println(s2);

//output: a2

String s3 = Stream.of(“d2”, “a2”, “b1”, “a3”, “c1”, “a4”, “a2”, “a1”)

        .filter(x -> x.startsWith(“a”))

        .findAny()

        .get();

    System.out.println(s3);

//output: a2

//换成并行流

String s4 = Stream.of(“d2”, “a2”, “b1”, “a3”, “c1”, “a4”, “a2”, “a1”)

        .parallel()

        .filter(x -> x.startsWith(“a”))

        .findFirst()

        .get();

    System.out.println(s4);

//output: a2

String s5 = Stream.of(“d2”, “a2”, “b1”, “a3”, “c1”, “a4”, “a2”, “a1”)

        .parallel()

        .filter(x -> x.startsWith(“a”))

        .findAny()

        .get();

    System.out.println(s5);

//output: a4

5. 归约

归约是一个比较复杂的数学理论,通常是用于将一个未知的问题转换成另一些已知问题,同时这些已知的问题和未知的问题存在某种关联。这里不做详细探讨。在Stream API有一些方法就是用的类似的归约的思想,将大的集合计算分解成小的函数计算并最终合成结果。

延伸阅读:

二、流是什么

流是一个很抽象的概念,只有多用才能理解。但是流绝对不是一个容器,这是一个典型的误解。容器可以被视为一个流,或者可以用流的方式来读写,但流不是容器。

譬如说,网络流(NetworkStream)就不是一个容器,也正因为流不是一个容器,所以流不存在拷贝。我们可以拷贝文件,拷贝内存,但是不能拷贝一个流。

同理,标准输入输出流显然也不是容器。如果是说.NET Framework中的Stream对象,其实我觉得他更像一个设备(Device)。这个设备提供或者不提供三个方法:读(Read )、写(Write)、检索(Seek),但是一个设备必须至少提供读或者写中的一个方法。

声明:本站稿件版权均属千锋教育所有,未经许可不得擅自转载。

猜你喜欢LIKE

深入理解I/O(阻塞、非阻塞,同步、异步)的概念及其区别?

2023-10-14

什么是设计模式?

2023-10-14

Linux下较好用的代码编辑器是什么?

2023-10-14

最新文章NEW

Stream是什么,有什么?

2023-10-14

Parceable和Serializable的区别?

2023-10-14

Linux GNU C和ANSI C有什么区别?

2023-10-14

相关推荐HOT

更多>>

快速通道 更多>>

最新开班信息 更多>>

网友热搜 更多>>