java的stram流操作

  • 为什么需要 Stream
  • 流与集合的区别
  • 对比:原始集合操作与Stream集合操作 (过滤/映射/扁平化/遍历/排序/去重/跳过/截断的应用)
  • 流的组成
  • 流操作的分类
  • 流的使用
  • 流操作常用操作
  • 前置钩子创建数据流
  • 过滤filter使用
  • map使用:将一个元素转换成另一个元素
  • flatMap使用:将一个对象转换成流
  • peek使用:遍历操作,与forEach类似,但不会销毁流元素
  • sort使用:排序,可选则自然排序或指定排序规则。有状态操作
  • distinct使用:对流元素进行去重。有状态操作
  • skip使用:跳过前N条记录,有状态操作
  • limit使用:截断前N条记录。有状态操作
  • allMatch使用:终端操作,短路操作。所有元素匹配,返回true
  • anyMatch使用:任何元素匹配,返回true
  • noneMatch使用:任何元素都不匹配,返回true
  • findFirst使用:找到第一个
  • findAny使用:找任意一个
  • max使用:找到最大的
  • min使用:找到最小的
  • count使用:计数
  • 流的四种构建形式
  • 由数值直接构建流
  • 通过数组构建流
  • 通过文件生成流
  • 通过函数生成流(无限流)
  • 流的预定义收集器
  • 集合收集器
  • 分组
  • 分区
  • 分组与分区的区别
  • 归约与汇总操作(涉及到并行执行)
  • 归约reduce
  • 汇总collect
  • 开源git库


为什么需要 Stream

Stream 作为 Java 8 的一大亮点,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。

  1. JDK1.8引入的新成员,以声明式方式处理集合数据
  2. 将基础操作连接起来,完成复杂的数据处理流水线
  3. 提供透明的并行处理

总结:从支持数据处理操作的源生成元素序列

流与集合的区别

  1. 时间与空间
  2. 只能遍历一次
  3. 外部迭代与内部迭代

对比:原始集合操作与Stream集合操作 (过滤/映射/扁平化/遍历/排序/去重/跳过/截断的应用)

  1. 以原始集合操作实现需求(举例:打印所有商品、图书类过滤掉、排序、要前面两个、求两件商品的总价、获取两件商品的名称、打印输入结果)
public void oldCartHandle() {
		//可以理解为获取数组对象为cartSkuList
        List<Sku> cartSkuList = CartService.getCartSkuList();

        /**
         * 1 打印所有商品
         */
        for (Sku sku : cartSkuList) {
            System.out.println(JSON.toJSONString(sku, true));
        }


        /**
         * 2 图书类过滤掉
         */
        List<Sku> notBooksSkuList = new ArrayList<Sku>();
        for (Sku sku : cartSkuList) {
            if (!SkuCategoryEnum.BOOKS.equals(sku.getSkuCategory())) {
                notBooksSkuList.add(sku);
            }
        }

        /**
         * 排序
         */
        notBooksSkuList.sort(new Comparator<Sku>() {
            @Override
            public int compare(Sku sku1, Sku sku2) {
                if (sku1.getTotalPrice() > sku2.getTotalPrice()) {
                    return -1;
                } else if (sku1.getTotalPrice() < sku2.getTotalPrice()) {
                    return 1;
                } else {
                    return 0;
                }
            }
        });
        /**
         * 前面两个
         */
        List<Sku> top2SkuList = new ArrayList<Sku>();
        for (int i = 0; i < 2; i++) {
            top2SkuList.add(notBooksSkuList.get(i));
        }

        /**
         * 4 求两件商品的总价
         */
        Double money = 0.0;
        for (Sku sku : top2SkuList) {
            // money = money + sku.getTotalPrice();
            money += sku.getTotalPrice();
        }


        /**
         * 获取两件商品的名称
         */
        List<String> resultSkuNameList = new ArrayList<String>();
        for (Sku sku : top2SkuList) {
            resultSkuNameList.add(sku.getSkuName());
        }
        /**
         * 打印输入结果
         */
        System.out.println(
                JSON.toJSONString(resultSkuNameList, true));
        System.out.println("商品总价:" + money);
    }
  1. 以Stream流方式实现需求
public void newCartHandle() {
        //多线程安全,防止多线程计数出现冲突,用于计算金额而声明的
        AtomicReference<Double> money =
                new AtomicReference<>(0.0);
        //CartService.getCartSkuList()可以理解为获取数组对象随后进入流操作
        List<String> resultSkuNameList =
                CartService.getCartSkuList()
                        .stream()
                        /**
                         * 1 打印商品信息
                         */
                        .peek(sku -> System.out.println(
                                JSON.toJSONString(sku, true)))
                        /**
                         * 2 过滤掉所有图书类商品
                         */
                        .filter(sku -> !SkuCategoryEnum.BOOKS.equals(
                                sku.getSkuCategory()))
                        /**
                         * 排序
                         */
                        .sorted(Comparator.
                                comparing(Sku::getTotalPrice).reversed())
                        /**
                         * TOP2
                         */
                        .limit(2)

                        /**
                         * 累加商品总金额
                         */
                        .peek(sku -> money.set(money.get() + sku.getTotalPrice()))

                        /**
                         * 获取商品名称
                         */
                        .map(sku -> sku.getSkuName())

                        /**
                         * 收集结果
                         */
                        .collect(Collectors.toList());


        /**
         * 打印输入结果
         */
        System.out.println(
                JSON.toJSONString(resultSkuNameList, true));
        System.out.println("商品总价:" + money.get());
    }

流的组成

Stream java 使用场景 java stream操作_System

流操作的分类

Stream java 使用场景 java stream操作_Stream java 使用场景_02

流的使用

Stream java 使用场景 java stream操作_JSON_03

流操作常用操作

前置钩子创建数据流

public class StreamOperator {

    List<Sku> list;

    /**
     * 可以理解为前缀执行获取数组对象
     */
    @Before
    public void init() {
        list = CartService.getCartSkuList();
    }

过滤filter使用

@Test
    public void filterTest() {
        list.stream()
                // filter
                .filter(sku ->
                        SkuCategoryEnum.BOOKS
                                .equals(sku.getSkuCategory()))

                .forEach(item ->
                        System.out.println(
                                JSON.toJSONString(
                                        item, true)));
    }

map使用:将一个元素转换成另一个元素

/**
     * map使用:将一个元素转换成另一个元素
     */
    @Test
    public void mapTest() {
        list.stream()

                // map
                .map(sku -> sku.getSkuName())

                .forEach(item ->
                        System.out.println(
                                JSON.toJSONString(
                                        item, true)));
    }

flatMap使用:将一个对象转换成流

@Test
    public void flatMapTest() {
        list.stream()

                // flatMap
                .flatMap(sku -> Arrays.stream(
                        sku.getSkuName().split("")))

                .forEach(item ->
                        System.out.println(
                                JSON.toJSONString(
                                        item, true)));
    }

peek使用:遍历操作,与forEach类似,但不会销毁流元素

@Test
    public void peek() {
        list.stream()

                // peek
                .peek(sku -> System.out.println(sku.getSkuName()))

                .forEach(item ->
                        System.out.println(
                                JSON.toJSONString(
                                        item, true)));
    }

sort使用:排序,可选则自然排序或指定排序规则。有状态操作

@Test
    public void sortTest() {
        list.stream()

                .peek(sku -> System.out.println(sku.getSkuName()))

                //sort
                .sorted(Comparator.comparing(Sku::getTotalPrice))

                .forEach(item ->
                        System.out.println(
                                JSON.toJSONString(
                                        item, true)));
    }

distinct使用:对流元素进行去重。有状态操作

@Test
    public void distinctTest() {
        list.stream()
                .map(sku -> sku.getSkuCategory())

                // distinct
                .distinct()

                .forEach(item ->
                        System.out.println(
                                JSON.toJSONString(
                                        item, true)));


    }

skip使用:跳过前N条记录,有状态操作

@Test
    public void skipTest() {
        list.stream()

                .sorted(Comparator.comparing(Sku::getTotalPrice))

                // skip
                .skip(3)

                .forEach(item ->
                        System.out.println(
                                JSON.toJSONString(
                                        item, true)));
    }

limit使用:截断前N条记录。有状态操作

/**
     * limit使用:截断前N条记录。有状态操作
     */
    @Test
    public void limitTest() {
        list.stream()
                .sorted(Comparator.comparing(Sku::getTotalPrice))

                .skip(2 * 3)

                // limit
                .limit(3)

                .forEach(item ->
                        System.out.println(
                                JSON.toJSONString(
                                        item, true)));
    }

allMatch使用:终端操作,短路操作。所有元素匹配,返回true

/**
     * allMatch使用:终端操作,短路操作。所有元素匹配,返回true
     */
    @Test
    public void allMatchTest() {
        boolean match = list.stream()

                .peek(sku -> System.out.println(sku.getSkuName()))

                // allMatch
                .allMatch(sku -> sku.getTotalPrice() > 100);

        System.out.println(match);
    }

anyMatch使用:任何元素匹配,返回true

/**
     * anyMatch使用:任何元素匹配,返回true
     */
    @Test
    public void anyMatchTest() {
        boolean match = list.stream()

                .peek(sku -> System.out.println(sku.getSkuName()))

                // anyMatch
                .anyMatch(sku -> sku.getTotalPrice() > 100);

        System.out.println(match);
    }

noneMatch使用:任何元素都不匹配,返回true

@Test
    public void noneMatchTest() {
        boolean match = list.stream()

                .peek(sku -> System.out.println(sku.getSkuName()))

                // noneMatch
                .noneMatch(sku -> sku.getTotalPrice() > 10_000);

        System.out.println(match);
    }

findFirst使用:找到第一个

@Test
    public void findFirstTest() {
        Optional<Sku> optional = list.stream()

                .peek(sku -> System.out.println(sku.getSkuName()))

                // findFirst
                .findFirst();

        System.out.println(
                JSON.toJSONString(optional.get(), true));
    }

findAny使用:找任意一个

/**
     * 找任意一个
     */
    @Test
    public void findAnyTest() {
        Optional<Sku> optional = list.stream()

                .peek(sku -> System.out.println(sku.getSkuName()))

                // findAny
                .findAny();

        System.out.println(
                JSON.toJSONString(optional.get(), true));
    }

max使用:找到最大的

@Test
    public void maxTest() {
        OptionalDouble optionalDouble = list.stream()
                // 获取总价
                .mapToDouble(Sku::getTotalPrice)

                .max();

        System.out.println(optionalDouble.getAsDouble());
    }

min使用:找到最小的

@Test
    public void minTest() {
        OptionalDouble optionalDouble = list.stream()
                // 获取总价
                .mapToDouble(Sku::getTotalPrice)

                .min();

        System.out.println(optionalDouble.getAsDouble());
    }

count使用:计数

@Test
    public void countTest() {
        long count = list.stream()
                .count();

        System.out.println(count);
    }
}

流的四种构建形式

由数值直接构建流

@Test
    public void streamFromValue() {
        Stream stream = Stream.of(1, 2, 3, 4, 5);

        stream.forEach(System.out::println);
    }

通过数组构建流

@Test
    public void streamFromArray() {
        int[] numbers = {1, 2, 3, 4, 5};

        IntStream stream = Arrays.stream(numbers);
        stream.forEach(System.out::println);
    }

通过文件生成流

@Test
    public void streamFromFile() throws IOException {
        // TODO 此处替换为本地文件的地址全路径
        String filePath = "";

        Stream<String> stream = Files.lines(
                Paths.get(filePath));

        stream.forEach(System.out::println);
    }

通过函数生成流(无限流)

@Test
    public void streamFromFunction() {
//        2,4,6,8...一直无限下去
//        Stream stream = Stream.iterate(0, n -> n + 2);

        Stream stream = Stream.generate(Math::random);
        stream.limit(100)
                .forEach(System.out::println);

    }

流的预定义收集器

集合收集器

/**
     * 集合收集器
     */
    @Test
    public void toList() {

        List<Sku> list = CartService.getCartSkuList();

        List<Sku> result = list.stream()
                .filter(sku -> sku.getTotalPrice() > 100)

                .collect(Collectors.toList());

        System.out.println(
                JSON.toJSONString(result, true));

    }

分组

@Test
    public void group() {
        List<Sku> list = CartService.getCartSkuList();

        // Map<分组条件,结果集合>
        Map<Object, List<Sku>> group = list.stream()
                .collect(
                        Collectors.groupingBy(
                                sku -> sku.getSkuCategory()));

        System.out.println(
                JSON.toJSONString(group, true));
    }

分区

@Test
    public void partition() {
        List<Sku> list = CartService.getCartSkuList();

        Map<Boolean, List<Sku>> partition = list.stream()
                .collect(Collectors.partitioningBy(
                        sku -> sku.getTotalPrice() > 100));

        System.out.println(
                JSON.toJSONString(partition, true));
    }

分组与分区的区别

Stream java 使用场景 java stream操作_Stream java 使用场景_04


Stream java 使用场景 java stream操作_Stream java 使用场景_05

归约与汇总操作(涉及到并行执行)

归约reduce

@Test
    public void reduceTest() {

        /**
         * 订单对象
         */
        @Data
        @AllArgsConstructor
        class Order {
            /**
             * 订单编号
             */
            private Integer id;
            /**
             * 商品数量
             */
            private Integer productCount;
            /**
             * 消费总金额
             */
            private Double totalAmount;
        }

        /*
            准备数据
         */
        ArrayList<Order> list = Lists.newArrayList();
        list.add(new Order(1, 2, 25.12));
        list.add(new Order(2, 5, 257.23));
        list.add(new Order(3, 3, 23332.12));

        /*
            以前的方式:
            1. 计算商品数量
            2. 计算消费总金额
         */

        /*
            汇总商品数量和总金额
         */
        Order order = list.stream()
                .parallel()
                .reduce(
                        // 初始化值
                        new Order(0, 0, 0.0),

                        // Stream中两个元素的计算逻辑
                        (Order order1, Order order2) -> {
                            System.out.println("执行 计算逻辑 方法!!!");

                            int productCount =
                                    order1.getProductCount()
                                            + order2.getProductCount();

                            double totalAmount =
                                    order1.getTotalAmount()
                                            + order2.getTotalAmount();

                            return new Order(0, productCount, totalAmount);
                        },

                        // 并行情况下,多个并行结果如何合并
                        (Order order1, Order order2) -> {
                            System.out.println("执行 合并 方法!!!");

                            int productCount =
                                    order1.getProductCount()
                                            + order2.getProductCount();

                            double totalAmount =
                                    order1.getTotalAmount()
                                            + order2.getTotalAmount();

                            return new Order(0, productCount, totalAmount);
                        });

        System.out.println(JSON.toJSONString(order, true));
    }

运行结果

执行 计算逻辑 方法!!!
执行 计算逻辑 方法!!!
执行 计算逻辑 方法!!!
执行 合并 方法!!!
执行 合并 方法!!!
{
	"id":0,
	"productCount":10,
	"totalAmount":23614.469999999998
}

汇总collect

@Test
    public void collectTest() {
        /**
         * 订单对象
         */
        @Data
        @AllArgsConstructor
        class Order {
            /**
             * 订单编号
             */
            private Integer id;
            /**
             * 用户账号
             */
            private String account;
            /**
             * 商品数量
             */
            private Integer productCount;
            /**
             * 消费总金额
             */
            private Double totalAmount;
        }

        /*
            准备数据
         */
        ArrayList<Order> list = Lists.newArrayList();
        list.add(new Order(1, "zhangxiaoxi", 2, 25.12));
        list.add(new Order(2, "zhangxiaoxi",5, 257.23));
        list.add(new Order(3, "lisi",3, 23332.12));

        /*
            Map<用户账号, 订单(数量和金额)>
         */

        Map<String, Order> collect = list.stream()
                .parallel()
                .collect(
                        () -> {
                            System.out.println("执行 初始化容器 操作!!!");

                            return new HashMap<String, Order>();
                        },
                        (HashMap<String, Order> map, Order newOrder) -> {
                            System.out.println("执行 新元素添加到容器 操作!!!");

                            /*
                                新元素的account已经在map中存在了
                                不存在
                             */
                            String account = newOrder.getAccount();

                            // 如果此账号已存在,将新订单数据累加上
                            if (map.containsKey(account)) {
                                Order order = map.get(account);
                                order.setProductCount(
                                        newOrder.getProductCount()
                                                + order.getProductCount());
                                order.setTotalAmount(
                                        newOrder.getTotalAmount()
                                                + order.getTotalAmount());
                            } else {
                                // 如果不存在,直接将新订单存入map
                                map.put(account, newOrder);
                            }

                        }, (HashMap<String, Order> map1, HashMap<String, Order> map2) -> {
                            System.out.println("执行 并行结果合并 操作!!!");

                            map2.forEach((key, value) -> {
                                map1.merge(key, value, (order1, order2) -> {

                                    // TODO 注意:一定要用map1做合并,因为最后collect返回的是map1
                                    return new Order(0, key,
                                            order1.getProductCount()
                                                    + order2.getProductCount(),
                                            order1.getTotalAmount()
                                                    + order2.getTotalAmount());
                                });
                            });
                        });

        System.out.println(JSON.toJSONString(collect, true));
    }

运行结果

执行 初始化容器 操作!!!
执行 初始化容器 操作!!!
执行 初始化容器 操作!!!
执行 新元素添加到容器 操作!!!
执行 新元素添加到容器 操作!!!
执行 新元素添加到容器 操作!!!
执行 并行结果合并 操作!!!
执行 并行结果合并 操作!!!
{
	"lisi":{
		"account":"lisi",
		"id":3,
		"productCount":3,
		"totalAmount":23332.12
	},
	"zhangxiaoxi":{
		"account":"zhangxiaoxi",
		"id":0,
		"productCount":7,
		"totalAmount":282.35
	}
}

开源git库

https://zhang5207892.coding.net/public/java996/bye-java996/git/files