java8流操作之不常用但是很好用的隐藏api

前言

1、一些普通的方式就不再多说了,这里主要说一些不常用的,但是作用很大的api方式

2、如果想要细致了解可以参考 JAVA8的流操作,十分推荐

一、flatMap

1、这个api主要是用来推平流的,和map不一致,map是对象到对象,而flatMap是把流平坦出来,具体如下,看注释就能明白了

2、我们看看 flatMap 展开或者叫扁平化操作,相当于 map+flat,通过 map 把每一个元素替换为一个流,然后展开这个流。比如,我们要统计所有订单的总价格,可以有两种方式:

  • 直接通过原始商品列表的商品个数 * 商品单价统计的话,可以先把订单通过 flatMap 展开成商品清单,也就是把 Order 替换为 Stream,然后对每一个 OrderItem 用 mapToDouble 转换获得商品总价,最后进行一次 sum 求和;
  • 利用 flatMapToDouble 方法把列表中每一项展开替换为一个 DoubleStream,也就是直接把每一个订单转换为每一个商品的总价,然后求和
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static java.util.Comparator.comparingDouble;
import static java.util.stream.Collectors.*;

class Scratch {
    public static void main(String[] args) {

        // 这种方式可以避免 两次的for循环,如果不这样,你得先遍历外层的order的list,再挨个遍历每隔order下的item。
        List<Order> orders = new ArrayList<>();
        //直接展开订单商品进行价格统计
        System.out.println(orders.stream()
                .flatMap(order -> order.getOrderItemList().stream())
                .mapToDouble(item -> item.getProductQuantity() * item.getProductPrice()).sum());

        //另一种方式flatMap+mapToDouble=flatMapToDouble
        System.out.println(orders.stream()
                .flatMapToDouble(order ->
                        order.getOrderItemList()
                                .stream().mapToDouble(item -> item.getProductQuantity() * item.getProductPrice()))
                .sum());
	}
}

@Data
class Order {
    private Long id;

    private Long customerId;//顾客ID

    private String customerName;//顾客姓名

    private List<OrderItem> orderItemList;//订单商品明细

    private Double totalPrice;//总价格

    private LocalDateTime placedAt;//下单时间

}

//订单商品类
@Data
@AllArgsConstructor
@NoArgsConstructor
class OrderItem {

    private Long productId;//商品ID

    private String productName;//商品名称

    private Double productPrice;//商品价格

    private Integer productQuantity;//商品数量

}

二、groupBy

1、基础操作说明这里就不再多说了,主要说的就说一些稍微不怎么用到的,但是其实作用很大的方式。

2、 groupBy 是分组统计操作,类似 SQL 中的 group by 子句。它和后面介绍的 partitioningBy 都是特殊的收集器,同样也是终结操作。分组操作比较复杂,为帮助大家理解得更透彻,我准备了 8 个案例


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static java.util.Comparator.comparingDouble;
import static java.util.stream.Collectors.*;

class Scratch {
    public static void main(String[] args) {

        //按照用户名分组,统计下单数量
        List<Map.Entry<String, Long>> collect1 = orders.stream().collect(groupingBy(Order::getCustomerName, counting()))
                .entrySet().stream().sorted(Map.Entry.<String, Long>comparingByValue().reversed()).collect(toList());
        System.out.println(collect1);


        //按照用户名分组,统计订单总金额
        System.out.println(orders.stream().collect(groupingBy(Order::getCustomerName, summingDouble(Order::getTotalPrice)))
                .entrySet().stream().sorted(Map.Entry.<String, Double>comparingByValue().reversed()).collect(toList()));


        //按照用户名分组,统计商品采购数量
        System.out.println(orders.stream().collect(groupingBy(Order::getCustomerName,
                        summingInt(
                                order -> order.getOrderItemList().stream()
                                .collect(summingInt(OrderItem::getProductQuantity))
                        )))
                .entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).collect(toList()));


        //统计最受欢迎的商品,倒序后取第一个
        orders.stream()
                .flatMap(order -> order.getOrderItemList().stream())
                .collect(groupingBy(OrderItem::getProductName, summingInt(OrderItem::getProductQuantity)))
                .entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
                .map(Map.Entry::getKey)
                .findFirst()
                .ifPresent(System.out::println);


        //统计最受欢迎的商品的另一种方式,直接利用maxBy
        orders.stream()
                .flatMap(order -> order.getOrderItemList().stream())
                .collect(groupingBy(OrderItem::getProductName, summingInt(OrderItem::getProductQuantity)))
                .entrySet().stream()
                .collect(maxBy(Map.Entry.comparingByValue()))
                .map(Map.Entry::getKey)
                .ifPresent(System.out::println);


        //按照用户名分组,选用户下的总金额最大的订单
        orders.stream().collect(groupingBy(Order::getCustomerName, collectingAndThen(maxBy(comparingDouble(Order::getTotalPrice)), Optional::get)))
                .forEach((k, v) -> System.out.println(k + "#" + v.getTotalPrice() + "@" + v.getPlacedAt()));


        //根据下单年月分组,统计订单ID列表
        System.out.println(orders.stream().collect
                (groupingBy(order -> order.getPlacedAt().format(DateTimeFormatter.ofPattern("yyyyMM")),
                        mapping(order -> order.getId(), toList()))));


        //根据下单年月+用户名两次分组,统计订单ID列表
        System.out.println(orders.stream().collect
                (groupingBy(order -> order.getPlacedAt().format(DateTimeFormatter.ofPattern("yyyyMM")),
                        groupingBy(order -> order.getCustomerName(),
                                mapping(order -> order.getId(), toList())))));
    }
}

@Data
class Order {
    private Long id;

    private Long customerId;//顾客ID

    private String customerName;//顾客姓名

    private List<OrderItem> orderItemList;//订单商品明细

    private Double totalPrice;//总价格

    private LocalDateTime placedAt;//下单时间

}

//订单商品类
@Data
@AllArgsConstructor
@NoArgsConstructor
class OrderItem {

    private Long productId;//商品ID

    private String productName;//商品名称

    private Double productPrice;//商品价格

    private Integer productQuantity;//商品数量

}


3、其中我们还是可以进行一些优化的,比如,mapxxx,但是其实你转化为int,就可以直接使用mapToIn的api,而且idea也会提示你,如下

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值