不得不说,使用Java Stream操作集合实在是太好用了,不过最近在观察生产环境错误日志时,发现偶尔会出现以下2个异常:

  1. java.lang.NullPointerException
  2. java.util.NoSuchElementException

因此本篇博客总结下使用Java Stream的部分场景以及如何避免上述的2个异常:

  1. 提取集合中的某一列(普通提取、去重)
  2. 按条件过滤集合
  3. 求和
  4. 最大值/最小值/平均值

1. 数据准备

首先定义下Friend类:

package com.zwwhnly.springbootaction.model;

import lombok.Data;

import java.math.BigDecimal;

@Data
public class Friend {
/**
* 姓名
*/
private String name; /**
* 年龄
*/
private Integer age; /**
* 身高
*/
private Long height; /**
* 所在城市
*/
private String city; /**
* 体重
*/
private BigDecimal weight; public Friend(String name, Integer age, Long height, String city, BigDecimal weight) {
this.name = name;
this.age = age;
this.height = height;
this.city = city;
this.weight = weight;
}
}

然后初始化以下数据供后面使用:

public static List<Friend> getFriendList() {
List<Friend> friendList = new ArrayList<>();
friendList.add(new Friend("小周", 28, 175L, "郑州", new BigDecimal("101.5")));
friendList.add(new Friend("小吴", 28, 170L, "洛阳", new BigDecimal("111.5")));
friendList.add(new Friend("小郑", 29, 176L, "郑州", new BigDecimal("121.5")));
friendList.add(new Friend("小王", 29, 180L, "北京", new BigDecimal("130")));
friendList.add(new Friend("小赵", 27, 178L, "苏州", new BigDecimal("140")));
friendList.add(new Friend("小钱", null, null, "杭州", new BigDecimal("150"))); return friendList;
}

2. 提取集合中的某一列

2.1 普通提取

比如,我们需要提取出所有朋友的姓名,可以使用Stream的map()方法,实现代码如下所示:

List<Friend> friendList = getFriendList();
List<String> nameList = friendList.stream().map(Friend::getName).collect(Collectors.toList()); nameList.forEach(name -> System.out.println(name));

输出结果:

小周

小吴

小郑

小王

小赵

2.2 提取后去重

比如,我们需要提取出所有朋友的年龄,但是需要去重,可以使用Stream的distinct()方法,实现代码如下所示:

List<Friend> friendList = getFriendList();
List<Integer> ageList = friendList.stream().map(Friend::getAge).distinct().collect(Collectors.toList()); ageList.forEach(age -> System.out.println(age));

输出结果:

28

29

27

3. 按条件过滤集合

比如,我们需要获取所有朋友中年龄在29岁以下,并且身高在170以上的朋友,可以调用filter方法,实现代码如下所示:

List<Friend> friendList = getFriendList();

List<Friend> youngPeople = friendList.stream()
.filter(friend -> friend.getAge() != null && friend.getAge() < 29 &&
friend.getHeight() != null && friend.getHeight() > 170L)
.collect(Collectors.toList());
System.out.println(youngPeople);

输出结果:

Friend(name=小周, age=28, height=175, city=郑州, weight=101.5)

Friend(name=小赵, age=27, height=178, city=苏州, weight=140)

4. 求和

4.1 Integer,Long,Double

比如,我们需要计算出所有朋友的年龄之和,可以调用mapToInt方法,实现代码如下所示:

List<Friend> friendList = getFriendList();

int ageSum = friendList.stream().filter(friend -> friend.getAge() != null).mapToInt(Friend::getAge).sum();
System.out.println(ageSum);

输出结果:

141

注意事项:

因为我们的age字段定义的是包装类型Integer,但求和之后的返回类型为基本类型int,所以在调用mapToInt方法之前,一定要过滤掉年龄为null的数据,否则分分钟抛异常。

比如,我们添加一条年龄为null的数据:

friendList.add(new Friend("小钱",null,178,"杭州"));

然后,我们不过滤null数据,直接调用mapToInt方法,就会抛出java.lang.NullPointerException异常:

List<Friend> friendList = getFriendList();

int ageSum = friendList.stream().mapToInt(Friend::getAge).sum();
System.out.println(ageSum);

如果字段类型是Long或者Double,可以调用相应的mapToDoublemapToLong,如下所示:

4.2 BigDecimal

和Integer、Long、Double类型不同,如果字段类型是BigDecimal,求和的话需要调用reduce方法,使用方法如下所示:

List<Friend> friendList = getFriendList();

BigDecimal weightSum = friendList.stream()
.filter(friend -> friend.getWeight() != null)
.map(Friend::getWeight)
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(weightSum);

输出结果:

754.5

注意事项:

为避免java.lang.NullPointerException异常,上面代码中的.filter(friend -> friend.getWeight() != null)也要记得加。

5. 最大值/最小值/平均值

5.1 Integer,Long,Double

比如,我们需要获取所有朋友中身高的最大值,实现代码如下所示:

List<Friend> friendList = getFriendList();

long heightMax = friendList.stream()
.filter(friend -> friend.getHeight() != null)
.mapToLong(Friend::getHeight)
.max().orElse(0);
System.out.println(heightMax);

输出结果:

180

注意事项:

因为max()方法的返回值是OptionalLong类型,所以我们需要继续调用orElse()方法设置个默认值,这里不要直接使用getAsLong()方法,因为当集合为空时,会抛出你肯定遇到过的java.util.NoSuchElementException异常:

long heightMax = friendList.stream()
.filter(friend -> friend.getHeight() != null)
.mapToLong(Friend::getHeight)
.max().getAsLong();

orElse()源码如下所示:

public long orElse(long other) {
return isPresent ? value : other;
}

getAsLong()源码如下所示:

public long getAsLong() {
if (!isPresent) {
throw new NoSuchElementException("No value present");
}
return value;
}

类似地,获取最小值的代码如下所示:

List<Friend> friendList = getFriendList();

long heightMin = friendList.stream()
.filter(friend -> friend.getHeight() != null)
.mapToLong(Friend::getHeight)
.min().orElse(0);
System.out.println(heightMin);

获取平均值的代码如下所示:

List<Friend> friendList = getFriendList();

double heightAverage = friendList.stream()
.filter(friend -> friend.getHeight() != null)
.mapToLong(Friend::getHeight)
.average().orElse(0D);
System.out.println(heightAverage);

5.2 BigDecimal

比如,我们需要获取所有朋友中体重的最大值,实现代码如下所示:

List<Friend> friendList = getFriendList();

BigDecimal weightMax = friendList.stream()
.filter(friend -> friend.getWeight() != null)
.map(Friend::getWeight)
.max(BigDecimal::compareTo)
.orElse(BigDecimal.ZERO);
System.out.println(weightMax);

输出结果:

150

注意事项:

1)为避免出现java.lang.NullPointerException异常,注意过滤体重为null的数据

2)因为max()方法的返回值为Optional类型,所以我们需要继续调用orElse()方法设置个默认值,这里不要直接使用get()方法,因为当集合为空时,会抛出你肯定遇到过的java.util.NoSuchElementException异常:

BigDecimal weightMax = friendList.stream()
.filter(friend -> friend.getWeight() != null)
.map(Friend::getWeight)
.max(BigDecimal::compareTo)
.get();

get()方法源码如下所示:

public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}

类似地,获取最小值的代码如下所示:

List<Friend> friendList = getFriendList();

BigDecimal weightMax = friendList.stream()
.filter(friend -> friend.getWeight() != null)
.map(Friend::getWeight)
.min(BigDecimal::compareTo)
.orElse(BigDecimal.ZERO);
System.out.println(weightMax);

6. 总结

使用Java Stream操作集合非常便利,但还是容易踩一些坑,比如文中提到的java.lang.NullPointerException异常和java.util.NoSuchElementException异常,所以使用时要多多注意,能不踩坑就不踩坑,就算踩坑,也别多次踩同一个坑。


使用Java Stream,提取集合中的某一列/按条件过滤集合/求和/最大值/最小值/平均值的更多相关文章

  1. java正则表达式提取地址中的ip和端口号

    由于我需要用到java正则表达式提取地址中的ip和端口号,所以我就写了一个demo,测试一下,下面是demo public class Test0810_1 { public static void ...

  2. 黑马基础阶段测试题:创建一个存储字符串的集合list,向list中添加以下字符串:”C++”、”Java”、” Python”、”大数据与云计算”。遍历集合,将长度小于5的字符串从集合中删除,删除成功后,打印集合中的所有元素

    package com.swift; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; ...

  3. Day_11【集合】扩展案例2_使用普通for循环获取集合中索引为3的元素并打印,统计集合中包含字符串"def"的数量,删除集合中的所有字符串",将集合中每个元素中的小写字母变成大写字母def",

    分析以下需求,并用代码实现 1.定义ArrayList集合,存入多个字符串"abc" "def" "efg" "def" ...

  4. java8 按条件过滤集合

    //黄色部分为过滤条件list.stream().filter(user-> user.getId() > 5 && "1组".equals(user. ...

  5. 使用Java正则表达式提取字符串中的数字一例

    直接上代码: String reg = "\\D+(\\d+)$"; //提取字符串末尾的数字:封妖塔守卫71 == >> 71 String s = monster. ...

  6. Java 正则表达式提取标签中的属性(src 连接地址)等

    public class Test { public static void main(String[] args) { String source = "<p><img ...

  7. java 正则提取字符串中的电话号码

    public static void test2() { String str = "张三:13539558064,李四:15626829748,赵六:13718952204"; ...

  8. java8 list转map,list集合中的元素的属性转set,list集合中对象的属性转list

    一.使用java8对list操作 1.1list转map private Map<String, Member> getMemberMap() { List<Member> m ...

  9. List<Map<String, Object>>集合中获取某个key并转换为List<Integer>集合

    package com.codyy.sso.controller.yuanqu; import java.util.ArrayList; import java.util.HashMap; impor ...

随机推荐

  1. mosquitto基于SSL/TLS安全认证测试MQTT

    一.环境搭建 1.mosquitto介绍 mosquitto是一个实现了MQTT3.1协议的代理服务器,由MQTT协议创始人之一的Andy Stanford-Clark开发,它为我们提供了非常棒的轻量 ...

  2. 使用vs2008的一个问题 2008-01-08 13:13

    昨天在写控制台版的一个管理系统.语言是:c++ ,开发工具是:vs 2008 .功能简单的描述一下,它是就是简单的查询,插入,删除等操作,操作的对象是一个xml文件. 这时我要需要一个命令行参数,根据 ...

  3. 洛谷 P6218 [USACO06NOV] Round Numbers S

    洛谷 P6218 [USACO06NOV] Round Numbers S 题目描述 如果一个正整数的二进制表示中,\(0\) 的数目不小于 \(1\) 的数目,那么它就被称为「圆数」. 例如,\(9 ...

  4. Scala中的isInstanceOf和asInstanceOf区别

    判断对象是否属于某个给定的类,可以用isInstanceOf方法:用asInstanceOf方法将引用转换为子类的引用. obj.isInstanceOf[T]就如同Java的obj instance ...

  5. JAVA——JVM参数设置规则以及参数含义

    JVM参数设置规则: -XX:+<option> 启用option,例如:-XX:+PrintGCDetails启动打印GC信息的选项,其中+号表示true,开启的意思-XX:-<o ...

  6. MySQL · 性能优化 · MySQL常见SQL错误用法(转自-阿里云云栖社区)

    作者:阿里云云栖社区链接:https://zhuanlan.zhihu.com/p/26043916来源:知乎著作权归作者所有,转载请联系作者获得授权. 前言 MySQL在2016年仍然保持强劲的数据 ...

  7. 第 1 篇 Scrum 冲刺博客(5.21)

    一.Alpha阶段认领的任务 二.明日任务安排 成员 5.22任务安排 工时 陈芝敏 调用小程序接口获取用户微信登录权限,初始化 1h 在页面显示倒计时 5h 冯晓凤 继续学习微信开放文档 5h 江晓 ...

  8. Java14版本特性【一文了解】

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...

  9. Redis主从复制(读写分离)

    主从复制(读写分离):读在从库读,写在主库写. 主从复制的好处:避免redis单点故障构建读写分离架构,满足读多写少的需求. 主从架构: 操作(启动实例,在一台机器上启动不同的实例,进行伪主从复制): ...

  10. 高效IO解决方案-Mmap「给你想要的快」

    随着技术的不断进步,计算机的速度越来越快.但是磁盘IO速度往往让欲哭无泪,和内存中的读取速度有着指数级的差距:然而由于互联网的普及,网民数量不断增加,对系统的性能带来了巨大的挑战,系统性能往往是无数技 ...