石家庄特色美食:还在用迭代器处置聚集吗?试试Stream,真香

admin 4个月前 (06-02) 科技 44 1

前言

上一篇博客一文带你深入领会 Lambda 表达式和方式引用我给人人先容了 Java8 函数式特征中的 Lambda,这篇文章我将继续讨论 stream 流的用法

声明:本文首发于博客园,作者:后青春期的Keats;地址:https://www.cnblogs.com/keatsCoder/ 转载请注明,谢谢!

Show Time

首先给人人看一段代码,让人人直观感受下 Java7 和 Java8 遍历处置聚集的差别

Dish 是一个菜肴工具,calories 属性示意该菜品的卡路里值,name 则是菜品的名称。我们需要过滤出卡路里小于400、然后凭据卡路里值升序、接着拿到他们的名称列表并返回

Java7

public static List<String> getLowCaloricDishesNamesInJava7(List<Dish> dishes){
    List<Dish> lowCaloricDishes = new ArrayList<>();
    for(Dish d: dishes){
        if(d.getCalories() < 400){
            lowCaloricDishes.add(d);
        }
    }
    List<String> lowCaloricDishesName = new ArrayList<>();
    Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
        public int compare(Dish d1, Dish d2){
            return Integer.compare(d1.getCalories(), d2.getCalories());
        }
    });
    for(Dish d: lowCaloricDishes){
        lowCaloricDishesName.add(d.getName());
    }
    return lowCaloricDishesName;
}

Java8

public static List<String> getLowCaloricDishesNamesInJava8(List<Dish> dishes){
    return dishes.stream()
        .filter(d -> d.getCalories() < 400)
        .sorted(comparing(Dish::getCalories))
        .map(Dish::getName)
        .collect(toList());
}

若是需要多核并行处置,则只需挪用 dishes.parallelStream() 即可

在 Java8 之前,程序员需要通过 2次遍历 + 一次聚集排序才气完成的事情,Java8 只需要一个链式挪用就可以解决。这就是 stream 的壮大之处

熟悉流

流是什么

流是 Java API 的新成员,允许程序员以声明式的方式处置聚集数据,而且支持链式挪用、支持并行处置。用流处置的聚集数据高效且易读。

流与聚集的异同

  1. 聚集的主要功能是以一定的时间和空间复杂度存储和接见元素,而流主要是用于元素盘算
  2. 聚集中的元素可以随意添加和删除,而流不能添加和删除元素
  3. 流的元素是按需盘算的,只有当用到时他才会介入盘算,而聚集中的元素必须提前全都准备好
  4. 流只能遍历一次,下面的代码会报错 java.lang.IllegalStateException: stream has already been operated upon or closed 流已经被消费掉
List<String> names = Arrays.asList("Java8", "Lambdas", "In", "Action");
Stream<String> s = names.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);
  1. 聚集接纳外部迭代,流接纳内部迭代。内部迭代意味着 Java 可以替你选择更优的迭代计谋和并行处置。而外部迭代若是程序员想着做个更有的迭代/接纳并行就相当于“下次一定”了

流操作分类

对流的操作可以分为两类,可以继续执行下一个流操作的称为中心操作(方式的返回值是 Stream),关闭流的操作称为终止操作。

中心操作

除非流水线上执行终端操作,否则中心操作不会执行任何处置。流会对中心操作举行合并、短路等优化

终端操作

终端操作会从流的流水线天生效果,返回一个非 Stream 的随便类型值

使用流

筛选和切片

筛选

filter(Predicate<? super T> predicate) 方式可以将流中知足某条件的元素筛选出来。该方式吸收一个谓词函数,返回流。比如要选出某个苹果聚集中红色的苹果

List<Apple> appleList = new ArrayList<>();
List<Apple> redAppleList = appleList.stream().filter(a -> "red".equals(a.getColor())).collect(Collectors.toList());

去重

distinct() 方式会凭据元素的 hashCode() 和 equals() 方式对流中元素举行去重操作

截断

limit(n) 方式会返回流的前 n 个元素,对于有序聚集List,流会根据添加顺序返回前 n 个元素,而无序聚集则不会

跳过

skip(n) 方式会跳过流的前 n 个元素,可以通过 skip(m).limit(n) 返回列表中第 m - (m+n) 区间的元素,类似与 mysql 中的 limit m,n

映射

对流中的每个元素应用函数

map(Function<? super T, ? extends R> mapper) 方式。该方式吸收一个 Function 函数,对流中的每一个元素使用。然后可以返回随便类型的工具。有了该方式,就可以连系 Lambda 表达式对聚集中的元素使用函数举行种种转换

流的扁平化

flatMap() 可以将流操作中多个流合并成一个流的多个元素。举个例子:聚集 words 有两个单词,现在想获得[H, e, l, o, W, r, d] 在 split 方式执行完毕后,返回的是 Stream(String[]) 工具,而此时若是执行 map 方式,返回的就是多个流的聚集(这个例子中就是两个 Stream(String)),这时是无法继续接下来的 distinct 操作的,因此需要 flatMap 将两个 Stream 扁平化成一个 Stream,然后举行操作

List<String> words = Arrays.asList("Hello", "World");

List<String> charList = words.stream().map(word -> word.split("")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());

该方式的方式声明 flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) 中可以看出,他所使用的函数式接口 Function 第二个泛型 R 必须是 Stream 流。即函数式接口的抽象方式返回值必须是 Stream 流及其子类工具。

查找和匹配

检查谓词是否至少匹配一个元素

anyMatch 方式可以回覆“流中是否存在至少一个复合谓词条件的元素”返回 boolean 类型的值,因此是一个终端操作,例如

List<Integer> num = Arrays.asList(1, 2, 3, 4, 5, 6);

if (num.stream().anyMatch(n -> n % 3 == 0)) {
    System.out.println("聚集中有元素是3的整数倍");
}

控制台会输出'聚集中有元素是3的整数倍',由于聚集中 3、6都是3的整数倍,相符谓词的条件

检查谓词是否匹配所有元素

allMatch 方式和 anyMatch 方式原理类似,然则它仅当所有元素知足谓词条件时,返回 true。

noneMatchallMatch 正好相反,仅当所有元素不知足谓词条件时,返回 true

ps:和 && || 运算符类似,以上三个操作都用到了短路的思想来提高效率。

查找元素

findAny() 该方式返回当前流中的随便元素,可以和其他流操作连系使用,这里需要注重 findAny() 返回的效果被 Optional 所包裹,Optional 是 Java8 为优雅的制止 NPE 所接纳的新 API,关于 Optional 的用法我会在下一篇博客和人人讨论,敬请期待。这里需要说明的就是 Optional.ifPresent(Consumer<? super T> consumer) 示意当 Optional 包裹的元素不为空时,执行 consumer

num.stream().filter(n -> n > 2).findAny().ifPresent(System.out::println);

findFirst() 该方式返回当前流中的第一个元素,一样平常也和其他流操作(例如 filter() 过滤)连系使用。与 findAny() 差别的是,他一定返回有序聚集的第一个知足条件的元素。固然有得必有失,作为价值,findFirst() 在并行处置时限制更多一些。

归约

元素求和

reduce(T identity, BinaryOperator<T> accumulator); 方式吸收两个参数:identity 初始值,accumulator 对两个数的操作。例如求聚集中数字的和:

num.stream().reduce(0, (a, b) -> a + b) // 盘算完成,返回 21

ps:Lambda 表达式 (a, b) -> a + b) 中 a 是上一轮执行完后的累计值,b 是本次循环流中的元素。通过累加就可以盘算出数字的和

最大值和最小值

reduce 方式不仅可以求和、求积。甚至可以盘算最大值、最小值。

num.stream().reduce(Integer::max);
num.stream().reduce(Integer::min);

总结

  1. 流是 Java API 的新成员,允许程序员以声明式的方式处置聚集数据,而且支持链式挪用、支持并行处置。用流处置的聚集数据高效且易读。
  2. 流的API中可以分为两大类,中心操作和终端操作,中心操作返回流工具,可以链式挪用,终端操作则返回非流工具。
  3. 流提供了许多利便的API,如筛选 filter、去重 distinct、截断 limit、跳过 skip、函数转换 map、扁平化 flatMap、判断流中是否有随便元素相符要求 anyMatch、是否所有元素都相符要求 allMatch、是否所有元素都不相符要求 noneMatch、查找元素 findAny findFirst、累计式的盘算元素 reduce

码字不易,若是你以为读完以后有收获,不妨点个推荐让更多的人看到吧!

,

阳光在线

阳光在线www.0913esg.com(原诚信在线)现已开放阳光在线手机版下载。阳光在线游戏公平、公开、公正,用实力赢取信誉。

皇冠体育声明:该文看法仅代表作者自己,与本平台无关。转载请注明:石家庄特色美食:还在用迭代器处置聚集吗?试试Stream,真香

网友评论

  • (*)

最新评论

  • 欧搏电脑版 2020-06-02 00:41:55 回复

    Allbet Gamingwww.boyijiaoyu888.com欢迎进入欧博平台网站(Allbet Gaming),Allbet Gaming开放欧博平台网址、欧博注册、欧博APP下载、欧博客户端下载、欧博真人游戏(百家乐)等业务。一眼万年啊

    1

文章归档

站点信息

  • 文章总数:755
  • 页面总数:0
  • 分类总数:8
  • 标签总数:1285
  • 评论总数:395
  • 浏览总数:23318