当前位置: 首页 > news >正文

Java8学习笔记之Stream API

摘要: 原创出处 https://peijie-sh.github.io 欢迎转载,保留摘要,谢谢!

Stream是Java8引入的一个重度使用lambda表达式的API。 Stream可以用流的方式处理数据集合,在Java8之前,我们处理这些集合,是需要迭代器的,比如iterator,这是外部迭代;而Stream是内部迭代,我们不用关心集合内部元素是如何迭代的,计算机会自动帮我们选择最适合的实现方式。

如何创建一个流

  1. 最常见的,有一个集合对象List<String> strs = Arrays.asList("Java 8 ", "Lambdas ", "In ", "Action");,直接调用strs.stream()就得到一个Stream<String>的流。 如果想使用并行流增加性能,请使用strs.parallelStream(),或strs.stream().parallel()
  2. 由值创建:Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
  3. 由数组创建:
        int[] numbers = {2, 3, 5, 7, 11, 13};
        int sum = Arrays.stream(numbers).sum();
复制代码
  1. 由文件创建:
        // 统计文本文件中有多少个不同的单词
        long uniqueWords = 0;
        try (Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())) {
            uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
                    .distinct()
                    .count();
        } catch (IOException e) {
        }
复制代码
  1. 由函数生成流:Stream.iterate()Stream.generate()可以生产无限流,即元素有无穷多个。一般来说,应该使用limit(n)来对这种流加以限制,以避免产生无穷多个元素。
    public void iterator() {
        Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
    }

    public void generate() {
        Stream.generate(Math::random).limit(5).forEach(System.out::println);
    }
复制代码

Stream常用方法

Stream API 支持两种类型的操作:中间操作(如filter或map)和终端操作(如count、findFirst、forEach和reduce)。

我用一个筛选菜单的需求作为示例。

准备工作

public class Dish {
        private final String name;
        private final boolean vegetarian;
        private final int calories;
        private final Type type;

        public Dish(String name, boolean vegetarian, int calories, Type type) {
            this.name = name;
            this.vegetarian = vegetarian;
            this.calories = calories;
            this.type = type;
        }

        public String getName() {
            return name;
        }

        public boolean isVegetarian() {
            return vegetarian;
        }

        public int getCalories() {
            return calories;
        }

        public Type getType() {
            return type;
        }

        @Override
        public String toString() {
            return name;
        }
    }

public enum Type {MEAT, FISH, OTHER}
复制代码
public List<Dish> init() {
        return Arrays.asList(
                new Dish("pork", false, 800, Type.MEAT),
                new Dish("beef", false, 700, Type.MEAT),
                new Dish("chicken", false, 400, Type.MEAT),
                new Dish("french fries", true, 530, Type.OTHER),
                new Dish("rice", true, 350, Type.OTHER),
                new Dish("season fruit", true, 120, Type.OTHER),
                new Dish("pizza", true, 550, Type.OTHER),
                new Dish("prawns", false, 300, Type.FISH),
                new Dish("salmon", false, 450, Type.FISH));
    }
复制代码

过滤筛选

谓词:返回boolean的函数

  • filter():接受一个谓词,返回符合条件的元素集合
    @Test
    public void filter() {
        List<Dish> menu = init();
        List<Dish> vegetarianMenu = menu.stream()
                .filter(Dish::isVegetarian)
                .collect(Collectors.toList());
        Assert.assertEquals(4, vegetarianMenu.size());
    }
复制代码
  • distinct():返回集合中各异的元素集合(去重)
    @Test
    public void distinct() {
        List<Integer> numbers = Arrays.asList(5, 1, 2, 1, 3, 3, 2, 4);
        numbers.stream().distinct().forEach(System.out::println);
    }
复制代码
  • limit():截取流中指定数量的元素,返回一个不超过给定长度的流。如果流是有序的,则最多会返回前n个元素。
    @Test
    public void limit() {
        List<Dish> menu = init();
        menu.stream()
                .filter(d -> d.getCalories() > 300)
                .limit(3)
                .forEach(System.out::println);
    }
复制代码
  • skip():跳过指定数量元素,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。
    @Test
    public void skip() {
        List<Dish> menu = init();
        menu.stream()
                .filter(d -> d.getCalories() > 300)
                .limit(3)
                .skip(2)
                .forEach(System.out::println);
    }
复制代码

映射

  • map():接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素。
    @Test
    public void map() {
        List<Dish> menu = init();
        List<String> dishNames = menu.stream().map(m -> m.getName()).collect(Collectors.toList());
    }
复制代码
  • flatMap():一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流,即扁平化为一个流。
    @Test
    public void flatMap() {
        String[] arrayOfWords = {"Goodbye", "World"};
        List<String> words = Arrays.asList(arrayOfWords);
        words.stream()
                .map(w -> w.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .forEach(System.out::println);
    }
复制代码

上面例子中,split()得到的是String[] 而不是String,因此各个数组并不是分别映射成一个流,而是映射成流的内容。所有使用map(Arrays::stream)时生成的单个流都被合并起来,变为一个流。

匹配

匹配比较简单,返回一个boolean

  • anyMatch():至少匹配一个
  • allMatch():全部匹配
  • noneMatch():全部不匹配,和allMatch相反
    @Test
    public void anyMatch() {
        List<Dish> menu = init();
        Assert.assertEquals(true, menu.stream().anyMatch(Dish::isVegetarian));
    }

    @Test
    public void allMatch() {
        List<Dish> menu = init();
        Assert.assertEquals(true, menu.stream().allMatch(d -> d.getCalories() < 1000));
    }

    @Test
    public void noneMatch() {
        List<Dish> menu = init();
        Assert.assertEquals(true, menu.stream().noneMatch(d -> d.getCalories() >= 1000));
    }
复制代码

查找

查找有2个方法:findFirst()findAny(),返回一个Optional<T>集合。 如果你不关心返回的元素是哪个,请使用findAny(),因为它在使用并行流时限制较少。

    @Test
    public void findFirst() {
        List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> firstSquareDivisibleByThree =
                someNumbers.stream()
                        .map(x -> x * x)
                        .filter(x -> x % 3 == 0)
                        .findFirst(); // 9
        System.out.println(firstSquareDivisibleByThree.get());
    }

    @Test
    public void findAny() {
        List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> firstSquareDivisibleByThree =
                someNumbers.stream()
                        .map(x -> x * x)
                        .filter(x -> x % 3 == 0)
                        .findAny(); // 9
        System.out.println(firstSquareDivisibleByThree.get());
    }
复制代码

归约

归约在汇总结合内所有数据的时候使用。比如求 max,min,sum。

    @Test
    public void reduce() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        int sum = numbers.stream().reduce(0, Integer::sum);
        System.out.println(sum);
    }
复制代码

原始类型流特化

流在内部迭代的过程中,对基本类型会自动装箱和拆箱。为了避免不需要的装箱拆箱,Java8提供了IntStreamDoubleStreamLongStream

  • 普通流转特化流:mapToInt(), mapToLong(), mapToDouble()
  • 特化流转普通流:boxed()
    public void boxedStream() {
        List<Dish> menu = init();
        // 特化
        IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
        // 转回普通Stream
        Stream<Integer> stream = intStream.boxed();
    }
复制代码

Java 8引入了两个可以用于IntStream和LongStream的静态方法,用于生成给定范围的数字流:

  • range(min, max):随机生成的数字不包含max,即(min, max)
  • rangeClosed(min, max):随机生成的数字包含max,即(min, max]

相关文章:

  • 国资委监事会主席季晓南:大数据金融发展要加强风险管控
  • 原生js获取、设置、删除属性
  • Linux安装mysql5.6
  • Python学习笔记__16.3章 UDP编程
  • 稳中有降 7月单路塔式服务器价格指导
  • Python入门知识
  • 云市场虽已硝烟弥漫但仍潜力巨大
  • Linux-系统启动和内核管理
  • memcache
  • 电商产品设计:会员系统
  • groupby()
  • 加固mysql服务器
  • Unity Excel转Json小工具excel2json
  • linux之scp命令
  • 详解Wifi模块对智能家居行业产生的影响
  • 【React系列】如何构建React应用程序
  • 【剑指offer】让抽象问题具体化
  • 002-读书笔记-JavaScript高级程序设计 在HTML中使用JavaScript
  • 2017 前端面试准备 - 收藏集 - 掘金
  • centos安装java运行环境jdk+tomcat
  • co模块的前端实现
  • httpie使用详解
  • iOS小技巧之UIImagePickerController实现头像选择
  • JavaScript DOM 10 - 滚动
  • Js基础——数据类型之Null和Undefined
  • js继承的实现方法
  • LeetCode541. Reverse String II -- 按步长反转字符串
  • Linux各目录及每个目录的详细介绍
  • quasar-framework cnodejs社区
  • React16时代,该用什么姿势写 React ?
  • ⭐ Unity 开发bug —— 打包后shader失效或者bug (我这里用Shader做两张图片的合并发现了问题)
  • vue:响应原理
  • 从零搭建Koa2 Server
  • 构造函数(constructor)与原型链(prototype)关系
  • 关于字符编码你应该知道的事情
  • 讲清楚之javascript作用域
  • 使用Swoole加速Laravel(正式环境中)
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 我的业余项目总结
  • 找一份好的前端工作,起点很重要
  • ionic异常记录
  • 进程与线程(三)——进程/线程间通信
  • 数据可视化之下发图实践
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #考研#计算机文化知识1(局域网及网络互联)
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (PWM呼吸灯)合泰开发板HT66F2390-----点灯大师
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (超简单)使用vuepress搭建自己的博客并部署到github pages上
  • (附源码)ssm高校实验室 毕业设计 800008
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (区间dp) (经典例题) 石子合并
  • (四)模仿学习-完成后台管理页面查询
  • (五)IO流之ByteArrayInput/OutputStream