首页 > 其他分享 >JTCR-Stream API-23

JTCR-Stream API-23

时间:2024-04-24 15:14:53浏览次数:29  
标签:JTCR Stream stream 流中 list System add API out

流基础

流是数据管道,表示一系列数据。流的操作是针对数据源来说的,但是流的操作不会改变数据源的数据,只会产生新的流。

最基础的流是 BaseStream 接口。

interface BaseStream<T, S extends BaseStream<T, S>>

T 表示流中数据的类型,S 表示扩展了 BaseStream 的流。BaseStream 扩展了 AutoCloseable 接口。

Stream 接口继承了 BaseStream 接口。

interface Stream<T>

T 表示流中数据的类型,任何引用类型数据都可以使用这个流。

流的操作分为两种,第一种是终止(terminal)操作,该操作会消耗流,产生一个结果,被消耗的流不能再次使用;另一种是中间(intermediate)操作,该操作会产生另一个流,可以用于创建管道,进行多个操作。中间操作不会立即执行,只有当终止操作需要执行时,前面的中间操作才会执行,称为 lazy behavior,这个机制可以提高执行效率。

中间操作又可以分为无状态(stateless)和有状态的(stateful)两种。无状态表示流中每个元素独立处理,与其他元素无关;有状态表示流中一个元素的处理依赖于其他元素。在并发处理流时操作有无状态很重要。

Stream 流中的元素必须是引用类型,为了处理原始类型,提供了三种继承自 BaseStream 的流接口,如下所示

  • DoubleStream
  • IntStream
  • LongStream

这些流除了用于处理原始类型,其他方面与 Stream 接口相似。

JDK8 开始,Collection 接口引入了 stream() 方法用于获取流。

// 返回流
default Stream<E>  stream();

// 如果可以,返回并发流;否则返回流
default Stream<E> parallelStream();

Arrays 类的方法 stream() 方法将数组作为流的数据源。形式之一为

static <T> Stream<T> stream(T[] array);

其他形式的 stream() 可以返回用于原始类型的三种流。

BufferedReader 的 lines() 方法返回流。

public static void m() {
  var list = new ArrayList<Integer>();
  for (int i = 0; i < 10; i++) {
    list.add(i);
  }
  Stream<Integer> s = list.stream();
  
  Optional<Integer> min = s.min(Integer::compare);
  if (min.isPresent()) {
    min.get();
  }
  
  s = list.stream();
  Optional<Integer> max = s.max(Integer::compare);
  if (max.isPresent()) {
    max.get();
  }
  
  list.stream().sorted().forEach(e -> System.out.print(e + " "));
  System.out.println();
  
  list.stream().filter(e -> (e % 2) == 1).filter(e -> e > 5)
    					 .forEach(e -> System.out.print(e + " "));
  System.out.println();
}

Optional 对象要么包含一个值要么为空。值的类型由 T 指定。

class Optional<T>

归约操作

归约操作(reduction operations)是对流进行处理,最终得到一个值的操作类型。流的 count() 方法返回流中数据的个数。reduce() 方法可以使用指定条件对流进行处理得到一个值。

Optional<T> reduce(BinaryOperator<T> accu);
T reduce(T i, BinaryOperator<T> accu);

T 表示流中数据类型,i 必须满足流中任一数据和它执行 accu 操作得到的结果是该数据。例如,若 accu 为加法,则 i = 0;如果 accu 为乘法,则 i = 1。BinaryOperator 接口继承自 BiFunction 接口,BiFunction 的抽象方法为

R apply(T v1, U v2)

BinaryOperator 的抽象方法为

T apply(T v1, T v2)

v1 根据 reduce() 方法参数的不同首次表示第一个元素或者 i 的值,v2 表示下一个元素。之后,v1 表示最近计算得到的结果,v2 表示下一个元素。accu 操作必须满足三个要求

  • 无状态
  • 无干扰:流不改变数据源
  • 结合性:类比数学中的结合律

结合性对于并发流的操作很重要。

public static void m() {
  var list = new ArrayList<Integer>();
  for (int i = 0; i < 10; i++) {
    list.add(i);
  }
  
  Optional<Integer> r1 = list.stream().reduce((a, b) -> a * b);
  if (r1.isPresent()) {
    r1.get();
  }
  
  int r2 = list.stream().reduce(1, (a, b) -> a * b);
}

除了 Collection 接口的 parallelStream() 方法获取并发流之外,流调用 BaseStream 接口的 parallel() 方法也可以获得并发流。当环境支持时,才能使用并发流;否则,并发流自动转变为流。

一般情况下,对并发流执行的操作必须满足结合性要求,其他两个要求也应该满足。

并发流一个特有的 reduce() 方法为

<U> U reduce(U i, BiFunction<U, ? super T, U> accu, BinaryOperator<U> comb);

其中,accu 用于归约操作,comb 用于将部分结果合并,得到最终的结果。

public static void m() {
  var list = new ArrayList<Integer>();
  for (int i = 0; i < 10; i++) {
    list.add(i);
  }
  double r = list.parallelStream().reduce(1.0, 
                                          (a, b) -> a * Math.sqrt(b), 
                                          (a, b) -> a * b);
}

并发流可以调用 BaseStream 的 sequential() 方法获得流。

使用并发流时,如果数据源的数据有序,那么流中数据有序。如果对并发流进行操作时顺序不重要,可以调用 BaseStream 接口的 unordered() 方法获得一个无序流,对无序流使用并发操作可以提高性能。

并发流使用 forEach() 方法不会保留流中数据的顺序,如果想按照顺序执行,使用 forEachOrdered() 方法。

Mapping

映射指通过某种规则将一个元素转换成另一个元素。通用方法为 map(),形式为

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

T 表示当前流中元素类型,R 表示返回流中元素类型。mapF 函数必须是无状态和无干扰的,Function 的抽象方法为

R apply(T val)
public static void m() {
  var list = new ArrayList<Integer>();
  for (int i = 0; i < 10; i++) {
    list.add(i);
  }
  double r = list.stream().map(e -> Math.sqrt(e)).reduce(1.0, (a, b) -> a * b);
}

map() 方法的其他版本如下

IntStream mapToInt(ToIntFunction<? super T> mapF);
LongStream mapToLong(ToLongFunction<? super T> mapF);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapF);

ToIntFunction 的方法为 applyAsInt(T v),必须返回 int 类型的结果。

public static void m() {
  var list = new ArrayList<Double>();
  list.add(1.2);
  list.add(3.9);
  list.add(8.1);
  list.add(2.4);
  
  list.stream()
      .mapToInt(e -> (int)Math.ceil(e)).forEach(e -> System.out.print(e + " "));
  System.out.println();
}

flatMap() 方法用于将一个由聚合元素组成的流拆分成由单个元素组成的流。有 flatMapToXX() 方法,XX 表示 Int/Long/Double。

flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
public static void m() {
  String[][] a = new String[][] {
    {"a", "b"},
    {"c", "d"}
  };
  var r = Arrays.stream(a).flatMap(Arrays::stream).toArray(String[]::new);
  Arrays.stream(a).flatMap(Arrays::stream).forEach(e -> System.out.print(e + " "));
}

Collecting

流的 collect() 方法提供了从流中获取数据构建集合的功能。一种形式如下

<R, A> R collect(Collector<? super T, A, R> colFun);

R 表示结果类型,A 表示中间结果类型,T 表示流中数据类型。Collector 接口定义如下,类型参数同前述

interface Collector<T, A, R>;

Collectors 类定义了若干返回 colletor 的静态方法。例如

static <T> Collector<T, ?, List<T>> toList();
static <T> Collector<T, ?, Set<T>> toSet();

分别返回将流中数据构建 List 和 Set 的 collector。

public static void m() {
  var list = new ArrayList<Integer>();
  for (int i = 0; i < 10; i++) {
    list.add(i);
  }
  var stream = list.stream().filter(e -> e % 2 == 0);
  List<Integer> r1 = stream.collect(Collectors.toList());
  Set<Integer> r2 = stream.collect(Collectors.toSet());
}

collect() 方法的另一种形式为

<R> R
  collect(Supplier<R> target, BiConsumer<R, ? super T> accu, BiConsumer<R, R> comb);

类似于 reduce() 方法。target 表示创建集合的方式,Supplier 的方法 get() 返回一个 R 类型的引用,accu 表示的 accept(T v1, U v2) 中,v1 表示集合,v2 表示元素;comb 表示的accept(T v1, U v2) 中,v1 和 v2 都表示集合。

LinkedList<Integer> r3 = stream.collect(() -> new LinkedList(),
                                       (a, b) -> a.add(b),
                                       (a, b) -> a.addAll(b));

HashSet<Integer> r4 = stream.collect(HashSet::new,
                                    HashSet::add,
                                    HashSet::addAll);

迭代器和流

流的 iterator() 方法返回和流关联的迭代器。

Iterator<T> iterator();

如果流中数据类型是原始类型,则返回与原始类型对应的流。

public static void m() {
  var list = new ArrayList<String>();
  list.add("al");
  list.add("bc");
  list.add("ed");
  
  Iterator<String> ite = list.stream().iterator();
  while (ite.hasNext()) {
    System.out.print(ite.next() + " ");
  }
  System.out.println();
}

使用 spliterator 迭代器遍历元素时,使用 tryAdvance() 方法,当有元素需要遍历时,执行遍历操作,遍历操作由参数提供,返回 true;没有下一个元素时,返回 false。

boolean tryAdvance(Consumer<? super T> action);
public static void m() {
  var list = new ArrayList<String>();
  list.add("al");
  list.add("bc");
  list.add("ed");
  
  Spliterator<String> ite = list.stream().spliterator();
  while (ite.tryAdvance(e -> System.out.print(e + " ")));
  System.out.println();
}

Spliterator 接口的 forEachRemaining() 方法接收一个 Consumer 函数式接口对象,对剩余的每个元素执行对象表示的操作。和 tryAdvance() 相比,不需要使用 while 语句。

default void forEachRemaining(Consumer<? super T> action);

Spliterator 接口的 trySplit() 方法将迭代器关联的数据划分成两份,一份由原迭代器关联,另一个份由返回的迭代器关联,并发编程时加快数据的处理。当迭代器不允许分割时,返回 null。

public static void m() {
  var list = new ArrayList<String>();
  list.add("al");
  list.add("bc");
  list.add("ed");
  
  Spliterator<String> ite = list.stream().spliterator();
  Spliterator<String> it2 = ite.trySplit();
  if (it2 != null) {
    it2.forEachRemaining(e -> System.out.println(e));
  }
  System.out.println();
  ite.forEachRemaining(e -> System.out.println(e));
}

参考

[1] Herbert Schildt, Java The Complete Reference 11th, 2019.
[2] https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
[3] java-8-flatmap-example

标签:JTCR,Stream,stream,流中,list,System,add,API,out
From: https://www.cnblogs.com/xdreamc/p/18155467

相关文章

  • JTCR-正则、反射和文本格式化-24 (end)
    正则Pattern类用于定义正则表达式,Matcher类用于匹配正则表达式。Pattern没有构造器,使用工厂方法compile()创建模式(pattern)。staticPatterncompile(Stringpattern)它将字符串转换成Matcher可以使用的正则表达式。Pattern的matcher()方法创建Matcher。Matcherm......
  • JTCR-网络-21
    InetAddressInetAddress类用于封装IP地址或者域名,支持IPv4和IPv6。创建InetAddress对象需要使用工厂方法,因为没有提供显式构造器。工厂方法如下staticInetAddressgetLocalhost();staticInetAddressgetByName(StringhostName);//一个域名对应多个IP地址时,返回......
  • JTCR-探究 NIO-20
    nio支持面向缓冲区、基于通道(channel-based)的I/O操作方法。JDK9开始,包java.nio及其子包位于java.base模块。nio子系统不是为了取代面向流的I/O类。NIO基础nio系统构建在缓冲区(buffers)和通道之上。缓冲区用于存放数据,通道表示一个已打开的与I/O设备的连接。通过......
  • MIGO BAPI BAPI_GOODSMVT_CREATE 各种类型使用汇总
    ***********GOODSMVT_CODE取值含义********01MB01*02MB31*03MB1A"发*04MB1B"转储*05MB1C"其它收货*06MB11*07MB04经常会遇到一些自定义的移动类型,但是并不知道对应的goodsmvt_code是多少。可以用如下方法进行查找首先去T158B中根据移动类......
  • API 开发的后盾:平台工程提供强力动态支持
    过去几年,开发团队一直在发展传统的DevOps。一些开发人员认为,CloudOps或DeploymentOps等新实践的兴起将会导致回到孤岛问题。其他人则不愿意在承担所有其他职责之外构建、部署、运行和维护运维。显然,确实需要新的云原生开发策略,而不是典型的DevOps。这就是平台工程的用武之地......
  • Google Play App Store API 采集谷歌安卓应用商城app的数据接口 - 2024最新
    iDataRiver平台https://www.idatariver.com/zh-cn/提供开箱即用的谷歌安卓应用商城googleplayappstore数据采集API,供用户按需调用。接口使用详情请参考GooglePlayAppStore接口文档接口列表1.获取指定app的基础信息参数类型是否必填默认值示例值描述apik......
  • ITMS-91053 Missing API declaration
    热烈欢迎,请直接点击!!!进入博主AppStore主页,下载使用各个作品!!!注:博主将坚持每月上线一个新app!!今天上传应用发现谈了一大堆警告,对于警告洁癖的我表示非常的震惊。基本上就是因为缺少隐私描述,但是我根本就没用第三方SDK啊,仔细一看发现是这两种:NSPrivacyAccessedAPICategoryUserDe......
  • DRF之请求执行流程和APIView源码分析
    DRF之请求执行流程和APIView源码分析【一】路由入口fromdjango.contribimportadminfromdjango.urlsimportpathfrombookimportviewsurlpatterns=[path('admin/',admin.site.urls),#原来的路由写法#path('test_http/',views.TestHttpResponse),......
  • SpringBoot整合OpenAPI 3生成在线接口文档
    SpringBoot整合springdoc-openapi,自动生成在线接口文档。1、引入pom依赖<dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.3.0</version></dependenc......
  • centos8报错错误:为 repo 'appstream' 下载元数据失败 : Cannot prepare internal mirr
    出现如下错误的错误:为repo‘appstream’下载元数据失败:Cannotprepareinternalmirrorlist:NoURLsinmirrorlist原因在2022年1月31日,CentOS团队终于从官方镜像中移除CentOS8的所有包。CentOS8已于2021年12月31日寿终正非,但软件包仍在官方镜像上保留了一段时间。现在......