1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Double max = taxList.stream().max(Comparator.comparing(Double::doubleValue)).get(); usersList.stream().map(Student::getSalary).max(BigDecimal::compareTo).get() ZfastCustomerOrder info = zfastCustomerOrderList.stream().max(Comparator.comparing(ZfastCustomerOrder::getTaxRate)).get(); tbypkcListF.add(kclist.stream().max(Comparator.comparing(TBYPKC::getDYXQ)).get()); Map<String, List<TBCFHZ>> map = cfhzlist.stream().collect(Collectors.groupingBy(TBCFHZ::getCMZH)); List<String> cfhlist = mapItem.getValue().stream().map(TBCFHZ::getCCFH).collect(Collectors.toList());
提取集合中的某一列/按条件过滤集合/求和/最大值/最小值/平均值。 不得不说,使用Java Stream操作集合实在是太好用了,不过最近在观察生产环境错误日志时,发现偶尔会出现以下2个异常:
java.lang.NullPointerException
java.util.NoSuchElementException
因此本篇博客总结下使用Java Stream的部分场景以及如何避免上述的2个异常:
提取集合中的某一列(普通提取、去重)
按条件过滤集合
求和
最大值/最小值/平均值
数据准备
首先定义下Friend类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @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; } }
初始化以下数据供后面使用:
1 2 3 4 5 6 7 8 9 10 11 12 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; }
普通提取 比如,我们需要提取出所有朋友的姓名 ,可以使用Stream的map()方法,实现代码如下所示:
1 2 3 4 List<Friend> friendList = getFriendList(); List<String> nameList = friendList.stream().map(Friend::getName).collect(Collectors.toList()); nameList.forEach(name -> System.out.println(name));
输出结果: 小周 小吴 小郑 小王 小赵
提取后去重 比如,我们需要提取出所有朋友的年龄,但是需要去重 ,可以使用Stream的distinct()方法,实现代码如下所示:
1 2 3 4 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
按条件过滤集合 比如,我们需要获取年龄在29岁以下,并且身高在170以上 的朋友,可以调用filter方法,实现代码如下所示:
1 2 3 4 5 6 7 8 9 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)
求和 Integer,Long,Double 比如,我们需要计算出所有朋友的年龄之和,可以调用mapToInt方法,实现代码如下所示:
List 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异常:
1 2 3 4 List<Friend> friendList = getFriendList(); int ageSum = friendList.stream().mapToInt(Friend::getAge).sum();System.out.println(ageSum);
如果字段类型是Long或者Double,可以调用相应的mapToDouble、mapToLong,如下所示:
BigDecimal 和Integer、Long、Double类型不同,如果字段类型是BigDecimal,求和的话需要调用reduce方法,使用方法如下所示:
1 2 3 4 5 6 7 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)也要记得加。
最大值/最小值/平均值
Integer,Long,Double
获取身高最大值: 1 2 3 4 5 6 7 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异常:
1 2 3 4 long heightMax = friendList.stream() .filter(friend -> friend.getHeight() != null ) .mapToLong(Friend::getHeight) .max().getAsLong();
orElse()源码如下所示:
1 2 3 public long orElse (long other) { return isPresent ? value : other; }
getAsLong()源码如下所示:
1 2 3 4 5 6 public long getAsLong () { if (!isPresent) { throw new NoSuchElementException("No value present" ); } return value; }
获取最小值: 1 2 3 4 5 6 7 List<Friend> friendList = getFriendList(); long heightMin = friendList.stream() .filter(friend -> friend.getHeight() != null ) .mapToLong(Friend::getHeight) .min().orElse(0 ); System.out.println(heightMin);
获取平均值: 1 2 3 4 5 6 7 List<Friend> friendList = getFriendList(); double heightAverage = friendList.stream() .filter(friend -> friend.getHeight() != null ) .mapToLong(Friend::getHeight) .average().orElse(0D ); System.out.println(heightAverage);
BigDecimal 比如,我们需要获取所有朋友中体重的最大值,实现代码如下所示:
1 2 3 4 5 6 7 8 9 10 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
注意:
为避免出现java.lang.NullPointerException异常,注意过滤体重为null的数据
因为max()方法的返回值为Optional类型,所以我们需要继续调用orElse()方法 设置个默认值,这里不要直接使用get()方法,因为当集合为空时,会抛出你肯定遇到过的java.util.NoSuchElementException异常:
1 2 3 4 5 BigDecimal weightMax = friendList.stream() .filter(friend -> friend.getWeight() != null ) .map(Friend::getWeight) .max(BigDecimal::compareTo) .get();
get()方法源码如下所示:
1 2 3 4 5 6 public T get () { if (value == null ) { throw new NoSuchElementException("No value present" ); } return value; }
获取最小值的代码重写: 1 2 3 4 5 6 7 8 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);
总结 使用Java Stream操作集合非常便利,但还是容易踩一些坑,比如文中提到的java.lang.NullPointerException异常和java.util.NoSuchElementException异常,所以使用时要多多注意,能不踩坑就不踩坑,就算踩坑,也别多次踩同一个坑。
实践
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @Test public void test05 () { List<Student> students = new ArrayList<>(); students.add(new Student(1 , "小王" , "天府大道一街" , new BigDecimal("2100" ), new BigDecimal("5000" ))); students.add(new Student(2 , "小王" , "天府大道二街" , new BigDecimal("2500" ), new BigDecimal("5000" ))); students.add(new Student(3 , "小红" , "天府大道三街" , new BigDecimal("2600" ), new BigDecimal("5000" ))); students.add(new Student(4 , "小明" , "天府大道四街" , new BigDecimal("4000" ), new BigDecimal("5000" ))); students.add(new Student(5 , "小王" , "天府大道五街" , new BigDecimal("3000" ), new BigDecimal("5000" ))); String name = "小王" ; Integer id = 2 ; System.out.println(students.size()); for (int i = 0 ; i < students.size(); i++) { System.out.println(students.get(i).toString() + " 索引值:" + i); } OptionalInt index = IntStream.range(0 , students.size()) .filter(i -> students.get(i).getName().equals(name)&&students.get(i).getId()==(id)) .findFirst(); System.out.println(index.getAsInt()); } @Test public void test07 () { List<Student> students = new ArrayList<>(); students.add(new Student(1 , "小王" , "天府大道一街" , new BigDecimal("2100" ), new BigDecimal("5000" ))); students.add(new Student(2 , "小王" , "天府大道二街" , new BigDecimal("2500" ), new BigDecimal("5000" ))); students.add(new Student(3 , "小红" , "天府大道三街" , new BigDecimal("2600" ), new BigDecimal("5000" ))); students.add(new Student(4 , "小明" , "天府大道四街" , new BigDecimal("4000" ), new BigDecimal("5000" ))); students.add(new Student(5 , "小王" , "天府大道五街" , new BigDecimal("3000" ), new BigDecimal("5000" ))); students.stream().sorted( Comparator.comparing(Student::getSalary) ).collect(Collectors.toList()).forEach( student -> System.out.println(student.toString())); }