详解Java8中流(Stream)的使用

写在前面:
我是「境里婆娑」。我还是从前那个少年,没有一丝丝改变,时间只不过是考验,种在心中信念丝毫未减,眼前这个少年,还是最初那张脸,面前再多艰险不退却。
写博客的目的就是分享给大家一起学习交流,如果您对 Java感兴趣,可以关注我,我们一起学习。

前言:还没接触过流的同学可以深入研究下此篇文章,让你在写代码过程中可以让你达到事半功倍效果。

一、回忆Lambda表达式

在学习流之前,我们先回忆一下Lambda表达式和函数式接口

使用 案例lambda例子对应函数式接口
布尔表达式(List< String> list -> list.isEmpty())Predicate<(List< String>>
创建对象() -> new Car()Supplier< Car>
消费一个对象(Car car) -> System.out.println(car.getColor)Consumer< Car>
从一个对象提取(String s) -> s.length()Function< String,Integer>
合并两个值(int a,int b) ->a*bIntBinaryOperator
比较两个值对象(Apple a1,Apple a2) -> a1.getWeigth().compareTo(a2.getWeigth())Comparator< Apple>或BiFunction< Apple,Apple,Integer>

二、什么是流

流是Java 新的API,它允许你以声明性方式处理数据集合。可以把流看作是遍历数据集合的高级迭代器。另外,流还可以透明的并行处理,无需写任何多线程代码。

public class Car {
    private String color;
    private String brand;
}    

例Java7之前,选出小汽车颜色代码如下:

List<Car> cars = Arrays.asList(new Car("bule", "aodi"), new Car("red", "BMW"), new Car("white", "benw"));
List<String> list = new ArrayList<>();
for (Car car : cars) {
   list.add(car.getColor());
}

例Java8之后可以如下:

List<String> collect = cars.stream().map(Car::getColor).collect(toList());

总结一下:Java8中的Stream API可以让你写出这样的代码

  • 声明性 更简洁,更易懂
  • 可复合,更灵活
  • 可并行,性能更好

看完上面代码我们还是有疑惑,流到底是什么呢?简短定义就是“从支持数据处理操作的源生成的元素序列”

  • 元素序列,就像集合一样,流提供一个接口,可以访问特定元素类型的有序值。集合是数据结构,目的是以特定的时间/空间复杂度存储和访问元素。但流的目的在于表达计算。总的来说集合讲的是数据,而流讲的是计算。
  • ,流会使用一个提供数据的源,如集合、 数组或输入输出资源。
  • 数据处理操作,流的数据处理功能支持类似数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort。流操作可以顺序执行,也可以并行执行。
  • 流水线,很多流操作本身会返回一个流,这样多个操作就可以连接起来,形成一个大的流水线。
  • 内部迭代,与使用迭代器显式迭代集合不同,流的迭代操作是在背后执行的。
1、流只能遍历一次

流还有一个特点就是只能遍历一次。遍历完之后,我们说这个流已经被消费掉了。

2、外部迭代和内部迭代

使用Collection接口需要用户去做迭代比如for-each,这称为外部迭代。相反,Streams库使用内部迭代。
采用内部迭代,项目可以透明地并行处理,或者用优化的顺序进行处理,要是使用 Java 过去的外部迭代方法,这些优化都是很困难的

三、流操作

1、谓词筛选

Streams接口支持filter方法,该操作会接受一个谓词作为参数。

List<Car> aodi = cars.stream().filter(car -> car.getBrand().equals("aodi")).collect(toList());
2、筛选各异元素

流还支持一个叫distinct的方法,它会返回一个元素各异的流。以下代码会筛选出所有基数。

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 != 0)
.distinct()
.forEach(System.out::println);
3、截段流

流支持limit方法,该方法会返回一个不超过给定长度的流。所需要的长度作为参数传递给limit。

List<Car> aodi = cars.stream().filter(car -> car.getBrand().equals("aodi")).limit(3).collect(toList());

请注意limit也可以用在无序流上。比如源是一个Set,这种情况下,limit的结果不会以人物顺序排列。

4、跳过元素

流还支持skip方法,返回一个扔掉了前n个元素的流。如果流中的元素不足n,则返回一个空流。注意。limit和skip是互补的。

List<Car> aodi = cars.stream().filter(car -> car.getBrand().equals("aodi")).skip(3).collect(toList());
5、map和flatMap

map和flatMap如何选用:假如你的集合流中包含子集合,那么使用flatMap可以返回该子集合的集合流

words.stream()
.map(word -> word.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(toList());

如果想详细了解map和flatMap区别请看这篇文章:java8中map和flatMap区别

6、查找和匹配

另一个常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性。

  • anyMatch,流中是否有一个元素能匹配到给定的谓词
if(cars.stream().anyMatch(car -> car.getBrand().equals("aodi"))) {
}
  • allMatch,是否匹配所有元素
if(cars.stream().allMatch(car -> car.getBrand().equals("aodi"))) {
}
  • noneMatch,确保流中没有任何元素与给定的谓词匹配
if(cars.stream().noneMatch(car -> car.getBrand().equals("aodi"))) {
}

allMatch、anyMatch、noneMatch都用到了我们所谓的短路,这就是大家熟悉java中的&&和||运算符。

  • findAny返回当前流中的任意元素。
Optional<Car> car = cars.stream().filter(car -> car.getBrand().equals("aodi")).findAny();
  • findFirst()查出第一个元素
Optional<Car> car = cars.stream().filter(car -> car.getBrand().equals("aodi")).findFirst();

findAny()和findFirst()都会返回一个Optional泛型的对象。

Optional 是一个容器类,代表一个值存在或不存在。Optional方法如下:

  • isPresent()将在Optional包含值的时候返回true。否则返回false
  • ifPresent()会在值存在的时候执行给定代码块。
  • get()会在值存在返回值。
  • orElse()会在值存在时返回值。否则返回一个默认值。
7、归约reduce

reduce是一个终端操作,比如我们做求和操作。

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

比如求最大值和最小值

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

至此我们把流的基本操作都介绍完。下面我们总结一下哪些是中间操作哪些是终端操作。
在这里插入图片描述
———————————————————————————

如果喜欢这篇文章的话请关注我!

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Java StreamJava 8 中新增的一个 API,用于处理集合数据。它提供了一种更加简洁、高效的方式来操作集合,可以让代码更加简洁、易读、易维护。下面是 Java Stream 的基本使用方法: 1. 创建流 Java Stream 可以从多种数据源中创建,比如集合、数组、文件等。例如,从集合创建一个 Stream 可以使用以下代码: ```java List<String> list = new ArrayList<>(); Stream<String> stream = list.stream(); ``` 2. 中间操作 中间操作是指对 Stream 进行操作,返回一个新的 Stream。常见的中间操作包括:filter、map、sorted、distinct 等。例如,下面的代码使用 filter 操作过滤出长度大于 5 的字符串: ```java List<String> list = Arrays.asList("apple", "banana", "orange", "watermelon"); Stream<String> stream = list.stream().filter(s -> s.length() > 5); ``` 3. 终止操作 终止操作是指对 Stream 进行最终的操作,返回一个非 Stream 的结果。常见的终止操作包括:forEach、reduce、collect、count 等。例如,下面的代码使用 forEach 操作输出过滤后的字符串: ```java List<String> list = Arrays.asList("apple", "banana", "orange", "watermelon"); list.stream().filter(s -> s.length() > 5).forEach(System.out::println); ``` 4. 短路操作 短路操作是指对 Stream 进行操作时,只要满足一定条件就可以停止操作,不必对整个流进行操作。常见的短路操作包括:findFirst、findAny、allMatch、anyMatch、noneMatch 等。例如,下面的代码使用 findAny 操作找到第一个长度大于 5 的字符串: ```java List<String> list = Arrays.asList("apple", "banana", "orange", "watermelon"); Optional<String> result = list.stream().filter(s -> s.length() > 5).findAny(); ``` 以上就是 Java Stream 的基本使用方法,需要注意的是,Stream 是一次性的,一旦进行了终止操作,就不能再对该 Stream 进行操作。同时,Stream 也是惰性求值的,只有在终止操作时才会进行真正的计算。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

境里婆娑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值