JDK8推出了很多新特性,比如:


JDK8添加了一个新接口类Stream,顶层集合类Collection对应添加了两个方法:stream()、parallelStream()。

public interface Collection<E> extends Iterable<E> {
	//...

	default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

	default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }

	//...
}

两个方法的区别:
1、stream()用于串行的流式计算,parallelStream()用于在数据量大时进行并行流式计算。
2、使用的区别不大,就是在开启流计算时根据操作的数据量选择调用stream()或者parallelStream(),到后面计算阶段才会决定使用哪种流计算方式。

Stream流式计算的好处,使编写代码更简洁。

性能嘛,通过实际示例对比了常规的集合类的过滤、封装、统计操作,几百的小数据量操作,常规for循环操作更快;数据量再大一点,stream()串行的流式计算会更快;上万级别的数据量后,parallelStream()并行流式计算会更快。在开启流计算时选择调用stream()或者parallelStream(),到后面计算阶段才会决定使用哪种流计算方式。


Collection.stream常用总结


分组统计Collectors.groupingBy()

演示类Student和数据

public class Student {

	private Integer id;
    private String name; //姓名
    private String sex;  //性别
    private int height;  //身高
    private String className; //班级名称

    public Student(Integer id, String name, String sex, int height, String className) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.height = height;
        this.className = className;
    }
	// ...省略get()/set()
}

public static void main(String[] args) {
	List<Student> studentList = Arrays.asList(
                new Student(1,"张三","男", 168, "一年级一班"),
                new Student(2,"李四","男", 182, "一年级一班"),
                new Student(3,"王五","男", 174, "一年级一班"),
                new Student(4,"赵六6","男", 186, "一年级二班"),
                new Student(5,"小红","女", 156, "一年级一班"),
                new Student(6,"小黄","女", 158, "一年级一班"),
                new Student(7,"小青6","女", 165, "一年级一班"),
                new Student(8,"小蓝6","女", 166, "一年级二班"),
                new Student(9,"小紫6","女", 172, "一年级二班"));
   //...
}

示例1:按性别分组

Map<String, List<Student>> map = studentList
                .stream()
                .collect(Collectors.groupingBy(Student::getSex));

返回结果:
在这里插入图片描述

示例2:按班级和性别分组

Map<String, List<Student>> map = studentList
                .stream()
                .collect(Collectors.groupingBy(e -> e.getClassName() + "_" + e.getSex()));

返回结果:
在这里插入图片描述


示例3:按身高1米6为界限分组

Map<String, List<Student>> map = studentList
                .stream()
                .collect(Collectors.groupingBy(item -> {
                    if(item.getHeight() < 160) {
                        return "身高偏低组";
                    }else {
                        return "身高正常组";
                    }
                }));

返回结果:
在这里插入图片描述


示例4:多级分组,分组后再次分组,示例1+示例3

先按性别分组,在每组里再按身高分成两组

Map<String, Map<String, List<Student>>> map = studentList
                .stream()
                .collect(Collectors.groupingBy(Student::getSex, Collectors.groupingBy(e -> {
                    if(e.getHeight() < 160) {
                        return "身高偏低组";
                    }else {
                        return "身高正常组";
                    }
                })));

返回结果:
在这里插入图片描述


示例5:分组求一行记录,求男女身高最高的学生

先按性别分组,再求每组里身高最高的一个学生。

Map<String, Student> map = studentList
                .stream()
                .collect(Collectors.groupingBy(
                        Student::getSex,
                        Collectors.collectingAndThen(
                                Collectors.maxBy(Comparator.comparingInt(Student::getHeight)),
                                Optional::get)));

返回结果:
在这里插入图片描述


示例6:分组求总数count,求男女生各多少人

Map<String, Long> map = studentList
                .stream()
                .collect(Collectors.groupingBy(
                        Student::getSex, 
                        Collectors.counting()));

返回结果:
在这里插入图片描述


示例7:分组求和sum,求男女各自分组的身高总和

Map<String, Long> map = studentList
                .stream()
                .collect(Collectors.groupingBy(
                        Student::getSex, 
                        Collectors.summingLong(Student::getHeight)));

返回结果:
在这里插入图片描述


示例8:分组求一列集合,求男女生姓名集合

Map<String, Set<String>> map = studentList
                .stream()
                .collect(Collectors.groupingBy(
                        Student::getSex,
                        Collectors.mapping(Student::getName, Collectors.toSet())));

返回结果:
在这里插入图片描述



求和:对List集合中的某个数字属性求和

上面 示例7 做了分组后求和,这里直接对对List集合中的某个属性求和。

示例:求所有水果的单价总和

public static void main(String[] args) {
    List<GoodsInfo> goodsList = Lists.newArrayList(
            new GoodsInfo("香蕉", new BigDecimal("5.5")),
            new GoodsInfo("苹果", new BigDecimal("8.5")),
            new GoodsInfo("橘子", new BigDecimal("6.5"))
    );

    BigDecimal value = goodsList.stream().map(GoodsInfo::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add);
    System.out.println(value); //运行结果:20.5
}

private static class GoodsInfo {
    private String name;     //水果名称
    private BigDecimal salary; //水果单价

    // ...省略get()/set()/构造方法


求最大最小值:对List集合中的某个数字属性求最大最小值

求最贵的水果、最便宜的水果

public static void main(String[] args) {
        List<GoodsInfo> goodsList = Lists.newArrayList(
                new GoodsInfo("香蕉", new BigDecimal("5.5")),
                new GoodsInfo("苹果", new BigDecimal("8.5")),
                new GoodsInfo("橘子", new BigDecimal("6.5"))
        );

        BigDecimal value = goodsList.stream().map(GoodsInfo::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add);
        System.out.println(value);

        // 最大值:最贵的水果
        BigDecimal maxSalary = goodsList.stream().
                collect(Collectors.maxBy(Comparator.comparing(GoodsInfo::getSalary))) //Optional<GoodsInfo>
                .get()  //GoodsInfo
                .getSalary();
        System.out.println(maxSalary); //运行结果:8.5

        // 最小值:最便宜的水果
        Optional<GoodsInfo> minGoods = goodsList.stream().
                collect(Collectors.minBy(Comparator.comparing(GoodsInfo::getSalary)));
        System.out.println(minGoods.get().getSalary()); //运行结果:5.5

    }


filter()过滤统计

示例1:求身高大于1米8的学生集合

List<Student> list = studentList
                .stream()
                .filter(s -> s.getHeight() > 180)
                .collect(Collectors.toList());

返回结果:
在这里插入图片描述


示例2:求身高大于1米8的学生集合的总人数count

long count = studentList
	              .stream()
	              .filter(e -> e.getHeight() > 180)
	              .count();

返回结果:
在这里插入图片描述


示例3:求身高大于1米8的学生集合的第一个

Student first = studentList
                .stream()
                .filter(e -> e.getHeight() > 180)
                .findFirst()
                .get();

返回结果:
在这里插入图片描述


示例4:求男生名字最长的长度

long maxLen = studentList
                .stream().filter(s -> s.getSex().equals("男"))
                .map(Student::getName)
                .mapToInt(String::length)
                .max()
                .getAsInt();

返回结果:
在这里插入图片描述



mapToInt()

示例1:求所有学生的身高总和sum

int totalHeight = studentsList
                      .stream()
                      .mapToInt(Student::getHeight)
                      .sum();

示例2:求最矮的学生min()

int minHeight = studentList
                .stream()
                .mapToInt(Student::getHeight)
                .min()
                .getAsInt();

示例3:求最高的学生max()

int maxHeight = studentList
                .stream()
                .mapToInt(Student::getHeight)
                .max()
                .getAsInt();


sorted()排序、limit()求Top值

示例1:按学生身高排序

// 倒序:从大到小排序
List<Student> sortList = studentList
                .stream()
                .sorted(Comparator.comparing(Student::getHeight).reversed())
                .collect(Collectors.toList());

// 顺序:从小到大排序
// 去掉reversed()即可

示例2:求身高最高的前3名学生

// 倒序,求Top3
List<Student> top3List = studentList
                .stream()
                .sorted(Comparator.comparing(Student::getHeight).reversed())
                .limit(3)
                .collect(Collectors.toList());

示例3:数字类型List排序、求Top值

List<BigDecimal> list = Lists.newArrayList();
list.add(new BigDecimal(-100));
list.add(new BigDecimal(-90));
list.add(new BigDecimal(1.1));
list.add(new BigDecimal(0));
list.add(new BigDecimal(50));
list.add(new BigDecimal(50.6));
// 倒序、求Top3
list = list.stream().sorted(Collections.reverseOrder()).limit(3).collect(Collectors.toList());

// 只能排序,顺序的话去掉Collections.reverseOrder()
// Collections.sort(list, Collections.reverseOrder());


提取List<对象>中一个字段成List<字段>、去重

示例一:提示List集合对象id成List结合

List<Integer> idList = studentList
                .stream()
                .map(Student::getId)
                .collect(Collectors.toList());

示例二:distinct()去重成List集合

List<Integer> idList2 = studentList
                .stream()
                .map(Student::getId)
                .distinct()
                .collect(Collectors.toList());

示例二:Collectors.toSet()去重成Set集合

Set<Integer> idList3 = studentList
                .stream()
                .map(Student::getId)
                .collect(Collectors.toSet());


去重:List集合不提取去重

上面示例中去重,是提取一个字段后,对重复的该字段去重,本示例去重后还是原List对象。

import static java.util.Comparator.comparingLong;
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toCollection;

// 根据id去重
List<Student> uniqueStudentList = studentList.stream().collect(
        Collectors.collectingAndThen(
                Collectors.toCollection(() -> new TreeSet<>(
                        Comparator.comparingLong(Student::getId))), ArrayList::new)
);
System.out.println(uniqueStudentList);


List转Map Collectors.toMap()

其实Collectors.groupingBy()也是一种List转Map。

示例一:学生ID和学生对象

// 学生ID和学生对象
Map<Integer, Student> idAndStudentMap = studentList
                .stream()
                .collect(Collectors.toMap(Student::getId, Function.identity()));

示例二:学生ID和学生姓名

// 学生ID和学生姓名
Map<Integer, String> idAndNameMap = studentList
                .stream()
                .collect(Collectors.toMap(Student::getId, Student::getName));

key值重复报Duplicate key错误的解决办法

Exception in thread "main" java.lang.IllegalStateException: Duplicate key {"id":1, "name":"张三","height":168,"sex":"男"}
	at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
	at java.util.HashMap.merge(HashMap.java:1254)
	at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
	at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
	at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
	at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
	at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
	at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
	at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)

解决方案是:

// 学生ID和学生对象
Map<Integer, Student> idAndStudentMap = studentList
                .stream()
                .collect(Collectors.toMap(
                        Student::getId,
                        Function.identity(),
                        (oldValue, newValue) -> newValue));

java8中转换成map时,提供了第三个参数,代表的是重复key的值该如何存放。上面的示例出现重复时,存放最后一次的value,此处可以根据需求自行处理。



Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐