乐字节-Java8新特性之Stream流(下)

接上一篇:《Java8新特性之stream》,下面继续接着讲Stream

5、流的中间操作

常见的流的中间操作,归为以下三大类:筛选和切片流操作、元素映射操作、元素排序操作:

1)、筛选和切片

filter(T -> boolean):保留 boolean 为 true 的元素

limit(long n):返回前 n 个元素

skip(long n):去除前 n 个元素

distinct():去除重复元素,这个方法是通过类的 equals 方法来判断两个元素是否相等的映射

map(T -> R):将流中的每一个元素 T

2)、映射为 R(类似类型转换)

flatMap(T -> Stream<R>): 将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流

3)、排序

sorted() / sorted((T, T) -> int):如果流中的元素的类实现了 Comparable 接口,即有自己的排序规则,那么可以直接调用 sorted() 方法对元素进行排序,如 Stream<Integer>

5.1、筛选和切片

例如以订单数据为例,在做报表展示时,会根据订单状态、用户信息、支付结果等状态来分别展示(即过滤和统计展示)

定义订单Order类

public class Order {

    // 订单id

    private Integer id;

    // 订单用户id

    private Integer userId;

    // 订单编号

    private  String orderNo;

    // 订单日期

    private Date orderDate;

    // 收货地址

    private String address;

    // 创建时间

    private Date createDate;

    // 更新时间

    private Date updateDate;

    // 订单状态  0-未支付  1-已支付  2-代发货  3-已发货  4-已接收  5-已完成

    private Integer status;

    // 是否有效  1-有效订单  0-无效订单

    private Integer isValid;


    //订单总金额

    private  Double total;

    /**

      此处省略getter/setter方法

    */

}

测试

public static void main(String[] args) {

        Order order01 = new Order(1,10,"20190301",

                new Date(),"上海市-浦东区",new Date(),new Date(),4,1,100.0);

        Order order02 = new Order(2,30,"20190302",

                new Date(),"北京市四惠区",new Date(),new Date(),1,1,2000.0);

        Order order03 = new Order(3,20,"20190303",

                new Date(),"北京市-朝阳区",new Date(),new Date(),4,1,500.0);

        Order order04 = new Order(4,40,"20190304",

                new Date(),"北京市-大兴区",new Date(),new Date(),4,0,256.0);

        Order order05 = new Order(5,40,"20190304",

                new Date(),"上海市-松江区",new Date(),new Date(),4,0,1000.0);

        List<Order> ordersList= Arrays.asList(order01,order02,order03,order04);

        // 过滤订单集合 有效订单 并打印到控制台

        ordersList.stream().filter((order)->order.getIsValid()==1).forEach(System.out::println);

    // 过滤订单集合有效订单 取前两条有效订单 并打印到控制台

        ordersList.stream().filter((order)->order.getIsValid()==1).limit(2).forEach(System.out::println);

    }

// 过滤订单集合有效订单 取最后一条记录

        ordersList.stream().filter((order)->order.getIsValid()==1)

                .skip(ordersList.size()-2).forEach(System.out::println);

// 去除订单编号重复的无效订单记录 此时因为比较的为Object Order对象需要重写HashCode 与Equals 方法

/**

    * 重写 equals 方法

    * @param obj

    * @return

    */

    @Override

    public boolean equals(Object obj) {

        boolean flag = false;

        if (obj == null) {

            return flag;

        }

        Order order = (Order) obj;

        if (this == order) {

            return true;

        } else {

            return (this.orderNo.equals(order.orderNo));

        }

    }

    /**

    * 重写hashcode方法

    * @return

    */

    @Override

    public int hashCode() {

        int hashno = 7;

        hashno = 13 * hashno + (orderNo == null ? 0 : orderNo.hashCode());

        return hashno;

    }

// 过滤订单集合无效订单 去除订单号重复记录

  ordersList.stream().filter((order)->order.getIsValid()==0).distinct().forEach(System.out::println);


5.2、映射

//过滤订单集合有效订单 获取所有订单订单编号

ordersList.stream().filter((order)->order.getIsValid()==1).map((order)->order.getOrderNo()).forEach(System.out::println);

// 过滤有效订单  并分离每个订单下收货地址市区信息

ordersList.stream().map(o->o.getAddress().split("-")).flatMap(Arrays::stream).forEach(System.out::println);


5.3、排序

//过滤有效订单 并根据用户id 进行排序

ordersList.stream().filter((order)->order.getIsValid()==1)

.sorted((o1,o2)->o1.getUserId()-o2.getUserId()).forEach(System.out::println);

//或者等价写法

ordersList.stream().filter((order)->order.getIsValid()==1)

                .sorted(Comparator.comparingInt(Order::getUserId)).forEach(System.out::println);

// 定制排序规则

/*过滤有效订单

* 定制排序:如果订单状态相同 根据订单创建时间排序 反之根据订单状态排序

*/

ordersList.stream().filter((order)->order.getIsValid()==1).sorted((o1,o2)->{

  if(o1.getStatus().equals(o2.getStatus())){

        return o1.getCreateDate().compareTo(o2.getCreateDate());

    }else{

        return o1.getStatus().compareTo(o2.getStatus());

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


6、流的终止操作

终止操作会从流的流水线生成结果。其结果是任何不是流的值,比如常见的List、 Integer,甚 至void等结果。 对于流的终止操作,分为以下三类:

1)、描述查找与匹配

allMatch:检查是否匹配所有元素;

anyMatch(T -> boolean): 流中是否有一个元素匹配给定的 T -> boolean 条件 ;

noneMatch(T -> boolean): 流中是否没有元素匹配给定的 T -> boolean 条件 ;

findAny():找到其中一个元素 (使用 stream() 时找到的是第一个元素;使用parallelStream() 并行时找到的是其中一个元素);

findFirst():找到第一个元素;

max():返回流中最大值;

min():返回流中最小值;

count():返回流中元素的总个数。

2)、归约: 将流中元素反复结合起来,得到一个值。

3)、reduce((T, T) -> T) 和 reduce(T, (T, T) -> T): 用于组合流中的元素,如求和,求积,求最大值等收集: 将流转换为其他形式,接收一个Collertor接口的实现,用于给Stream中元素做汇总的方法collect()

6.1、查找与匹配

// 筛选所有有效订单 匹配用户id =20 的所有订单

System.out.println("allMatch匹配结果:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).allMatch((o) -> o.getUserId() == 20));

System.out.println("anyMatch匹配结果:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).anyMatch((o) -> o.getUserId() == 20));

System.out.println("noneMatch匹配结果:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).noneMatch((o) -> o.getUserId() == 20));

//  筛选所有有效订单 返回订单总数

System.out.println("count结果:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).count());

// 筛选所有有效订单 返回金额最大订单值

Optional<Double> max=ordersList.stream().filter((order) -> order.getIsValid() == 1)

    .map(Order::getTotal).max(Double::compare);

System.out.println("订单金额最大值:"+max.get());

// 筛选所有有效订单 返回金额最小订单值

Optional<Double> min=ordersList.stream().filter((order) -> order.getIsValid() == 1)

    .map(Order::getTotal).min(Double::compare);

System.out.println("订单金额最小值:"+min.get());


6.2、归约

将流中元素反复结合起来,得到一个值的操作。

// 归约操作 计算有效订单总金额

System.out.println("有效订单总金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1).map(Order::getTotal).reduce(Double::sum).get());


6.3、Collector收集数据

6.3.1、收集

将流转换为其他形式,coollect 方法作为终端操作, 接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。最常用的方法,把流中所有元素收集到一个 List, Set 或 Collection 中

toList

toSet

toCollection

toMap

// 收集操作

// 筛选所有有效订单 并收集订单列表

List<Order> orders= ordersList.stream().filter((order) -> order.getIsValid() == 1).collect(Collectors.toList());

orders.forEach(System.out::println);

// 筛选所有有效订单 并收集订单号 与 订单金额

Map<String,Double> map=ordersList.stream().filter((order) -> order.getIsValid() == 1).

    collect(Collectors.toMap(Order::getOrderNo, Order::getTotal));

// java8 下对map 进行遍历操作 如果 Map 的 Key 重复了,会报错

map.forEach((k,v)->{

    System.out.println("k:"+k+":v:"+v);

});


6.3.2、汇总

countintg():用于计算总和

count():用于计算总和(推荐使用,写法更简洁)

summingInt() ,summingLong(),summingDouble():用于计算总和

averagingInt(),averagingLong(),averagingDouble()用于平均

summarizingInt,summarizingLong,summarizingDouble 同样可以实现计算总和,平均等操作,比如summarizingInt 结果会返回IntSummaryStatistics 类型 ,然后通过get方法获取对应汇总值即可

// 汇总操作

//筛选所有有效订单 返回订单总数

System.out.println("count结果:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).collect(Collectors.counting()));

System.out.println("count结果:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).count());

//  返回订单总金额

System.out.println("订单总金额:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).collect(Collectors.summarizingDouble(Order::getTotal)));

System.out.println("订单总金额:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).mapToDouble(Order::getTotal).sum());

System.out.println("订单总金额:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).map(Order::getTotal).reduce(Double::sum).get());

// 返回 用户id=20 有效订单平均每笔消息金额

System.out.println("用户id=20 有效订单平均每笔消费金额:"+ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).

                  filter((order -> order.getUserId()==20))

                  .collect(Collectors.averagingDouble(Order::getTotal)));

System.out.println("用户id=20 有效订单平均每笔消费金额:"+

                  ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).

                  filter((order -> order.getUserId()==20))

                  .mapToDouble(Order::getTotal).average().getAsDouble());

System.out.println("用户id=20 有效订单平均每笔消费金额:"+

                  ordersList.stream().

                  filter((order) -> order.getIsValid() == 1).

                  filter((order -> order.getUserId()==20))

                  .collect(Collectors.summarizingDouble(Order::getTotal)).getAverage());

// 筛选所有有效订单 并计算订单总金额

System.out.println("订单总金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)

                  .collect(Collectors.summingDouble(Order::getTotal)));

// 筛选所有有效订单 并计算最小订单金额

System.out.println("最小订单金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)

                  .map(Order::getTotal).collect(Collectors.minBy(Double::compare)));

// 筛选所有有效订单 并计算最大订单金额

System.out.println("最大订单金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)

                  .map(Order::getTotal).collect(Collectors.maxBy(Double::compare)));


6.3.3、最值

maxBy,minBy 两个方法,需要一个 Comparator 接口作为参数,实现最大 最小值获取操作

// 取最会

// 筛选所有有效订单 并计算最小订单金额

System.out.println("最小订单金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)

                  .map(Order::getTotal).collect(Collectors.minBy(Double::compare)));

// 筛选所有有效订单 并计算最大订单金额

System.out.println("最大订单金额:"+ordersList.stream().filter((order) -> order.getIsValid() == 1)

                  .map(Order::getTotal).collect(Collectors.maxBy(Double::compare)));


6.3.4、分组

groupingBy 用于将数据分组,最终返回一个 Map 类型

groupingBy 可以接受一个第二参数实现多级分组

// 分组-根据有效订单支付状态进行分组操作

Map<Integer,List<Order>> g01=ordersList.stream().filter((order) -> order.getIsValid() == 1)

    .collect(Collectors.groupingBy(Order::getStatus));

g01.forEach((status,order)->{

    System.out.println("----------------");

    System.out.println("订单状态:"+status);

    order.forEach(System.out::println);

});

// 分组-查询有效订单 根据用户id 和 支付状态进行分组

Map<Integer,Map<String,List<Order>>> g02= ordersList.stream().filter((order) -> order.getIsValid() == 1)

    .collect(Collectors.groupingBy(Order::getUserId,Collectors.groupingBy((o)->{

        if(o.getStatus()==0){

            return "未支付";

        }else if (o.getStatus()==1){

            return "已支付";

        }else if (o.getStatus()==2){

            return "待发货";

        }else if (o.getStatus()==3){

            return "已发货";

        }else if (o.getStatus()==4){

            return "已接收";

        } else{

            return "已完成";

        }

    })));

g02.forEach((userId,m)->{

    System.out.println("用户id:"+userId+"-->有效订单如下:");

    m.forEach((status,os)->{

        System.out.println("状态:"+status+"---订单列表如下:");

        os.forEach(System.out::println);

    });

    System.out.println("-----------------------");

});


6.3.5、partitioningBy 分区

分区与分组的区别在于,分区是按照 true 和 false 来分的,因此partitioningBy 接受的参数的 lambda 也是 T -> boolean

// 分区操作 筛选订单金额>1000 的有效订单

Map<Boolean,List<Order>> g03= ordersList.stream().filter((order) -> order.getIsValid() == 1)

    .collect(Collectors.partitioningBy((o)->o.getTotal()>1000));

g03.forEach((b,os)->{

    System.out.println("分区结果:"+b+"--列表结果:");

    os.forEach(System.out::println);

});

// 拼接操作 筛选有效订单 并进行拼接

String orderStr=ordersList.stream().filter((order) -> order.getIsValid() == 1).map(Order::getOrderNo)

    .collect(Collectors.joining(","));

System.out.println(orderStr);


乐字节-Java新特性之stream流就介绍到这里了,接下来小乐还会接着给大家讲解Java8新特性之Optional

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容