使用流

在本篇中,你将会看到Stream API支持的许多操作。这些操作能让你快速完成复杂的数据查询,如筛选、切片、映射、查找、匹配、和归约。接下来,我们会看看一些特殊的流:数值流、来自文件和数组等多种来源的流,最后是无限流。

  1. 筛选和切片
  • 用谓词筛选(筛选):Stream接口支持filter方法。该操作会接受一个谓词(一个返回Boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流。
  • 筛选各异的元素(去重):流还支持一个叫做distinct的方法,它会返回一个元素各异(根据流所生成元素的hashCode和equals方法的实现)的流。
  • 截短流(截取):流支持limit(n)方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit。如果流是有序的,则最多会返回前n个元素。
  • 跳过元素(跳过):流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。请注意,limit(n)和skip(n)是互补的!
  1. 映射
  • 对流中每一个元素应用函数:流支持map方法,它会接受一个函数(Function)作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是创建一个“新版本”,而不是去“修改”)。
  • 流的扁平化:使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Array::Stream)时生成的单个流都被合并起来,即扁平化为一个流。
  1. 查找和匹配
  • 检查谓词是否至少匹配一个元素:anyMatch方法可以回答“流中是否有一个元素能匹配给定的谓词”。
  • 检查谓词是否匹配所有元素:allMatch方法的工作原理和anyMatch类似,但它会看看流中的元素是否都能匹配给定的谓词。和allMatch相对的是noneMatch。它可以确保流中没有任何元素与给定的谓词匹配。
  • 查找元素:findAny方法将返回当前流中的任意元素。它可以与其他流操作结合使用。
  • 查找第一个元素:有些流有一个出现顺序来指定流中项目出现的逻辑顺序(比如由List或排序好的数据列生成的流)。对于这种流,你可能想要找到第一个元素。为此有一个findFirst方法,它的工作方式类似于findAny。
  1. 归约
  • 元素求和

int sum = numbers.stream().reduce(0, (a, b) -> a + b);

  • 一个初始值,这里是0;
  • 一个BinaryOperator((T,T) -> T)来将两个元素结合起来产生一个新值,这里我们用的是lambda (a,b) -> a+b。 reduce接受两个参数:

你可以使用方法引用让这段代码更简洁。在Java8里,Integer类现在有一个静态的sum方法来对两个数求和,这恰好是我们想要的,用不着反复用Lambda写同一段代码了:

int sum = numbers.stream().reduce(0, Integer::sum);

reduce还有一个重载的变体,它不接受初始值,但是会返回一个Optional对象:

Optional<Integer> sum = numbers.stream().reduce(Integer::sum);

  • 最大值和最小值

原来,只要用归约就可以计算最大值和最小值了!让我们来看看如何利用刚刚学到的reduce来计算流中最大或最小的元素。正如你前面看到的,reduce接受两个参数:

  • 一个初始值;
  • 一个Lambda来把两个流元素结合起来并产生一个新值
Optional<Integer> max = number.stream().reduce(Integer::max);
Optional<Integer> max = number.stream().reduce(Integer::min);
复制代码

当然你也可以写成Lambda (x, y) -> x < y ? x : y

  1. 数值流
  • 原始类型流特化

  Java8引入了三个原始类型特化流接口:IntStream、DoubleStream和LongStream,分别将流中的元素特化为int、long和double,从而避免了暗含的装箱成本。每个接口都带来了进行常用数值归约的新方法,比如对数值流求和的sum,找到最大元素的max。此外还有在必要时再把它们转换回对象流的方法。要记住的是,这些特化的原因并不在于流的复杂性,而是装箱造成的复杂性--即类似int和Integer之间的效率差异。

  将流映射为特化版本的常用方法是mapToInt、mapToDouble和mapToLong。这些方法和前面说的map方法的工作方式一样,只是它们返回的是一个特化流,而不是Stream<T>。例如:

int calories = menu.stream()
    .mapToInt(Dish::getCalories)
    .sum();
复制代码

  这里,mapToInt会从每道菜中提取热量(用一个Integer表示),并返回一个IntStream(而不是一个Stream<Integer>)。然后你就可以调用IntStream接口中定义的sum方法,对卡路里求和了!请注意,如果流是空的,sum默认返回0。IntStream还支持其他的方便方法,如max、min、average等。

  同样,一旦有了数值流,你可能回想把它转换回非特化流。例如,IntStream上的操作只能产生原始整数:IntStream的map操作接受的Lambda必须接受int并返回int(一个IntUnaryOperator)。但是你可能想生成另一类值,比如Dish。为此,你需要访问Stream接口中定义的那些更广义的操作。要把原始流转换为一般流(每个int都会装箱成一个Integer),可以使用boxed方法:

IntStream intStream = menu.stream().map(Dish::getCalories);
Stream<Integer> stream = intStream.boxed();
复制代码

  对于三种原始特化流,也分别有一个Optional原始类型特化版本:OptionalInt、OptionalDouble和OptionalLong。

  例如,要找到IntStream中的最大元素,可以调用max方法,它会返回一个OptionalInt:

OptionalInt maxCalories = menu.stream()
    .mapToInt(Dish::getColories)
    .max();
复制代码

  现在,如果没有最大值的话,你就可以显式处理OptionalInt去定义一个默认值了:

int max = maxCalories.orElse(1);//如果没有最大值,显式提供一个默认对大值

  • 数值范围

  Java8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range和rangeClosed。这两个方法都是第一个参数接受起始值,第二个参数接受结束值。但是range是不包含结束值的,而rangeClosed包含结束值。

  1. 构建流
  • 由值构建流

  你可以使用静态方法Stream.of,通过显式值创建一个流。它可以接受任意数量的参数。例如,以下代码直接使用Stream.of创建一个字符串流。然后,你可以将字符串转换为大写,再一个一个打印出来:

Stream<String> stream = Stream.of("Java 8", "Lambdas", "In", "Action");
stream.map(String::toUpperCase).forEach(System.out::println);
复制代码

  你可以使用empty得到一个空流:

Stream<String> emptyStream = Stream.empty();

  • 由数组创建流

  你可以使用静态方法Arrays.Stream从数组创建一个流。它接受一个数组作为参数。例如,你可以将一个原始类型int的数组转换为IntStream:

int[] numbers = {2, 3, 4, 5, 6}; int sum = Arrays.stream(numbers).sum();

  • 由文件生成

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

    • 迭代(iterator)

    我们先来看一个iterator的简单例子,然后在解释:

    Stream.iterator(0, n -> n + 2)
        .limit(10)
        .forEach(System.out::println);
    复制代码

      iterator方法接受一个初始值(在这里是0),还有一个依次应用在每个产生的新值上的Lambda(UnaryOperator类型)。这里,我们使用Lambda n -> n + 2,返回的是前一个元素加上2。因此,iterator方法生成了一个所有正偶数的流:流的第一个元素是初始值0。请注意,此操作将生成一个无限流--这个流没有结尾,因为值是按需计算的,可以永远计算下去。我们使用limit方法来显式限制流的大小。这里只选择了前10个偶数,然后可以调用forEach终端操作来消费流,并分别打印每个元素。

    • 生成(generate)

    与iterator方法类似,generate方法也可让你按需生成一个无限流。但generate不是依次对每个新生成的值应用函数的。它接受一个Supplier<T>类型的Lambda提供新的值。我们先来看一个简单的用法:

    Stream.generate(Math::random).limit(5).forEach(System.out::println);

    这段代码将生成一个流,其中有五个0到1之间的随机双精度数。

转载于:https://juejin.im/post/5b88f0ebf265da437174c45c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用上传文件到MinIO时,需要注意以下参数: 1. Bucket名称:上传的文件需要存储在哪个Bucket中。 2. Object名称:上传的文件保存时使用的Object名称。 3. 文件:上传文件的数据,可以使用标准输入或文件等。 4. 文件大小:上传文件的大小限制。 5. 文件类型:上传文件的类型限制,可以使用MIME类型或文件扩展名等。 6. 访问控制:上传文件时需要指定访问控制权限,如公共读或私有读写等。 以下是使用Python SDK进行上传文件到MinIO的示例代码: ```python import io from minio import Minio from minio.error import S3Error # 创建MinIO客户端对象 client = Minio( "minio.example.com", access_key="ACCESS_KEY", secret_key="SECRET_KEY", secure=False ) # 上传文件到MinIO try: # 将文件数据存储到BytesIO对象中 data = b"Hello, World!" file_data = io.BytesIO(data) # 上传文件到MinIO client.put_object( bucket_name="my-bucket", object_name="hello.txt", data=file_data, length=len(data), content_type="text/plain", metadata={ "x-amz-acl": "public-read" } ) print("File uploaded successfully.") except S3Error as e: print("Error uploading file: {}".format(e)) ``` 在上面的示例代码中,我们使用MinIO Python SDK中的`put_object()`方法将文件上传到MinIO。该方法需要指定Bucket名称、Object名称、数据、数据长度、文件类型和访问控制等参数。需要注意的是,如果上传的文件大小超过了服务器端的配置限制,则会上传失败。因此,需要根据实际情况来设置文件大小和类型限制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值