Java8使用Stream对集合的操作集锦

本文收集了网络上Stream流的用法和我在项目中的实际使用案例,不对其原理进行描述(俺也不会,哈哈)。
以下使用有错误、疑问或者更好的写法欢迎评论区讨论学习。
同时也欢迎各位大佬评论区留下本文中未提及的好用的api方法讨论学习。

创建业务逻辑测试实体类。

在这里插入图片描述

加入测试数据,以代码示例都以此测试数据为依据。

	public static List<User> getUserList() {
	    List<User> list = new ArrayList<>();
	    list.add(User.builder().id(1L).userName("剑帝").age(18).height(BigDecimal.valueOf(170.7)).createTime(DateUtil.parseDateTime("2023-05-21 17:17:17")).build());
	    list.add(User.builder().id(2L).userName("街霸").age(20).height(BigDecimal.valueOf(165.6)).createTime(DateUtil.parseDateTime("2023-05-20 18:17:17")).build());
	    list.add(User.builder().id(3L).userName("红眼").age(25).height(BigDecimal.valueOf(185.7)).createTime(DateUtil.parseDateTime("2023-06-21 18:17:17")).build());
	    list.add(User.builder().id(4L).userName("瞎子").age(27).height(BigDecimal.valueOf(180.5)).createTime(DateUtil.parseDateTime("2023-05-30 17:17:17")).build());
	    list.add(User.builder().id(5L).userName("枪炮师").age(23).height(BigDecimal.valueOf(175.7)).createTime(DateUtil.parseDateTime("2022-05-21 17:17:17")).build());
	
	    return list;
	}

1、去重方法

需要注意的是 distinct() 在不修改对象重写的equals和hashCode方法情况下,对象中的每个属性值必须完全相等才会被去重。
如果需要对某个属性进行去重的话可以重写equals和hashCode方法,或者使用如下写法。(这也是网上给出的普遍方法,我个人认为这个方法有一点瑕疵,就是新的数据和旧数据顺序不一致了,欢迎评论区留下更好的方法!)
【示例】在测试数据基础上新增去重的测试数据

	public static void main(String[] args) {
		// 获取测试数据
	    List<User> list = getUserList();
	    list.add(User.builder().id(7L).userName("枪炮师").age(23).height(BigDecimal.valueOf(175.7)).createTime(DateUtil.parseDateTime("2022-05-21 17:17:17")).build());
	
	//    List<User> userList = list.stream().distinct().collect(Collectors.toList());
	
	    List<User> userList = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
	            new TreeSet<>(Comparator.comparing(User::getUserName))), ArrayList::new));
	    userList.forEach(System.err::println);
	
	}

运行结果:
在这里插入图片描述

2、内存分页方法

在一些复杂的业务场景下,数据库分页往往达不到需求,这时候就需要手动分页,此api还是很好的,比之前手动计算好太多了。
【示例】limit(long n) 方法用于返回前n条数据,skip(long n) 方法用于跳过前n条数据。

	public static void main(String[] args) {
		// 获取测试数据
		List<User> list = getUserList();
		
		// current为页码(当前页),pageSize为每页显示条数,此示例是展示第二页的数据
		long current = 2; // 根据实际情况代入参数,此处仅为展示效果
		long pageSize = 3; // 根据实际情况代入参数,此处仅为展示效果
		List<User> userList = list.stream().skip((current - 1) * pageSize).limit(pageSize).collect(Collectors.toList());
		userList.forEach(System.err::println);
	}

执行结果:
在这里插入图片描述

3、过滤方法

【示例】过滤出来年龄大于20并且身高大于180的数据。(其他字段按照需求写对应的判断即可)

	public static void main(String[] args) {
		// 获取测试数据
		List<User> list = getUserList();
		
		List<User> userList = list.stream().filter(e -> e.getAge() > 20 && e.getHeight().compareTo(new BigDecimal("180")) > 0).collect(Collectors.toList());
		userList.forEach(System.err::println);
	}

执行结果:
在这里插入图片描述

4、数据处理方法

使用场景很灵活,下面仅列举了一些我使用过的场景。

4.1 map()

常用于把对象中的某个元素转换为一个新的集合。
【示例】把对象中的主键id转换为一个新的集合

	public static void main(String[] args) {
		// 获取测试数据
		List<User> list = getUserList();
		
		List<Long> idList = list.stream().map(User::getId).collect(Collectors.toList());
		idList.forEach(System.err::println);
	}

执行结果:
在这里插入图片描述

4.2 flatMap

将流中的每一个元素 T 映射为一个流,再把每一个流连接成为一个流。
常用于中间数据处理。
【示例】收集对象中的属性值成为一个新的集合

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        Set<String> resultSet = list.stream().flatMap(e -> {
            HashSet<String> set = new HashSet<>();
            set.add(e.getId().toString());
            set.add(e.getAge().toString());
            return set.stream();
        }).collect(Collectors.toSet());

        resultSet.forEach(System.err::println);
    }

执行结果:
在这里插入图片描述
【示例】将map中的value处理为一个新的集合

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 造测试map,仅为测试使用,无特殊意义。
        Map<String, List<User>> map = new HashMap<>();
        map.put(list.get(0).getUserName(), list.subList(0, 2));
        map.put(list.get(1).getUserName(), list.subList(2, 4));
        map.forEach((k, v) -> System.err.println(k + "--------" + v));

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

        List<Integer> ageList = map.values().stream().flatMap(Collection::stream).map(User::getAge).collect(Collectors.toList());
        ageList.forEach(System.err::println);
    }

执行结果:
在这里插入图片描述

5、判断方法

【示例】

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 判断集合中是否存在年龄大于18的元素
        boolean result1 = list.stream().anyMatch(e -> e.getAge() > 18);
        // 判断集合中否存在用户名称是都包含 "剑" 的元素
        boolean result2 = list.stream().allMatch(e -> e.getUserName().contains("剑"));
        // 判断集合中是否不存在身高大于185的元素
        boolean result3 = list.stream().noneMatch(e -> e.getHeight().compareTo(new BigDecimal("185")) > 0);

        System.err.println(result1);
        System.err.println(result2);
        System.err.println(result3);
    }

执行结果:
在这里插入图片描述

6、统计方法

6.1 reduce()

【示例】使用 reduce() 求用户列表中年龄的最大值、最小值、总和。

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        Integer maxAge = list.stream().map(User::getAge).reduce(Integer::max).get();
        Integer minAge = list.stream().map(User::getAge).reduce(Integer::min).get();
        Integer sumAge = list.stream().map(User::getAge).reduce(Integer::sum).get();

        System.err.println("最大年龄:" + maxAge);
        System.err.println("最小年龄:" + minAge);
        System.err.println("年龄总和:" + sumAge);
    }

执行结果:
在这里插入图片描述

6.2 mapToInt(T -> int) 、mapToDouble(T -> double) 、mapToLong(T -> long)

int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum);计算元素总和的方法其中暗含了装箱成本,map(User::getAge) 方法过后流变成了 Stream 类型,而每个 Integer 都要拆箱成一个原始类型再进行 sum 方法求和,这样大大影响了效率。针对这个问题 Java 8 有良心地引入了数值流 IntStream, DoubleStream, LongStream,这种流中的元素都是原始数据类型,分别是 int,double,long。
流转换为数值流:

  • mapToInt(T -> int) : return IntStream
  • mapToDouble(T -> double) : return DoubleStream
  • mapToLong(T -> long) : return LongStream

【示例】使用 mapToInt() 求用户列表中年龄的最大值、最小值、总和、平均值。

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        int maxAge = list.stream().mapToInt(User::getAge).max().getAsInt();
        int minAge = list.stream().mapToInt(User::getAge).min().getAsInt();
        int sumAge = list.stream().mapToInt(User::getAge).sum();
        double aveAge = list.stream().mapToInt(User::getAge).average().getAsDouble();

        System.err.println("最大年龄:" + maxAge);
        System.err.println("最小年龄:" + minAge);
        System.err.println("年龄总和:" + sumAge);
        System.err.println("平均年龄:" + aveAge);

    }

执行结果:
在这里插入图片描述

3.3 counting() 和 count()

【示例】

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 统计用户名中包含 "剑" 的人数,使用 counting() 方法进行统计
        Long nameCount = list.stream().filter(user -> user.getUserName().contains("剑")).collect(Collectors.counting());

        // 统计25岁以上的人数,使用 count() 方法进行统计(推荐)
        Long ageCount = list.stream().filter(user -> user.getAge() >= 25).count();

        System.err.println("用户名中包含 '剑' 的人数:" + nameCount + "人");
        System.err.println("25岁以上的人数:" + ageCount + "人");

    }

执行结果:
在这里插入图片描述

3.4 summingInt()、summingLong()、summingDouble()

计算总和。
【示例】计算年龄总和

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 计算年龄总和(不推荐使用,idea都提示了)
        int sumAge = list.stream().collect(Collectors.summingInt(User::getAge));

        System.err.println("年龄总和:" + sumAge);

    }

执行结果:
在这里插入图片描述

3.5 averagingInt()、averagingLong()、averagingDouble()

用于计算平均值。使用方法同上,不再赘述。

3.6 summarizingInt()、summarizingLong()、summarizingDouble()

这三个方法比较特殊,比如 summarizingInt 会返回 IntSummaryStatistics 类型。
IntSummaryStatistics类提供了用于计算的平均值、总数、最大值、最小值、总和等方法,方法如下图:
在这里插入图片描述
【示例】使用 IntSummaryStatistics 统计:最大值、最小值、总和、平均值、总数。

public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 获取IntSummaryStatistics对象
        IntSummaryStatistics statistics = list.stream().collect(Collectors.summarizingInt(User::getAge));

        // 统计:最大值、最小值、总和、平均值、总数
        System.err.println("最大年龄:" + statistics.getMax());
        System.err.println("最小年龄:" + statistics.getMin());
        System.err.println("年龄总和:" + statistics.getSum());
        System.err.println("平均年龄:" + statistics.getAverage());
        System.err.println("用户总数:" + statistics.getCount());
        
    }

执行结果:
在这里插入图片描述

3.7 BigDecimal 类型的统计

【示例】用户身高信息统计

 	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 最高身高
        BigDecimal maxHeight = list.stream().map(User::getHeight).reduce(BigDecimal::max).orElse(BigDecimal.ZERO);

        // 最低身高
        BigDecimal minHeight = list.stream().map(User::getHeight).reduce(BigDecimal::min).orElse(BigDecimal.ZERO);

        // 身高总和
        BigDecimal sumHeight = list.stream().map(User::getHeight).reduce(BigDecimal.ZERO, BigDecimal::add);

        // 平均身高
        BigDecimal avgHeight = list.stream().map(User::getHeight).reduce(BigDecimal.ZERO, BigDecimal::add).divide(new BigDecimal(list.size()));

        // 打印统计结果
        System.err.println("最高身高:" + maxHeight + "cm");
        System.err.println("最低身高:" + minHeight + "cm");
        System.err.println("身高总和:" + sumHeight + "cm");
        System.err.println("平均身高:" + avgHeight + "cm");
    }

执行结果:
在这里插入图片描述

7、排序方法

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

4.1 普通排序

  • comparingInt
  • comparingLong
  • comparingDouble

【示例】使用comparingInt()对年龄进行排序

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 根据年龄排序(正序)
//        List<User> resultList = list.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList());

        // 根据年龄排序(倒序)
        List<User> resultList = list.stream().sorted(Comparator.comparingInt(User::getAge).reversed()).collect(Collectors.toList());

        resultList.forEach(System.err::println);
    }

执行结果:
在这里插入图片描述

4.2 对时间进行排序

【示例】

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 根据创建时间排序(正序)
//        List<User> resultList = list.stream().sorted(Comparator.comparing(User::getCreateTime)).collect(Collectors.toList());

        // 根据创建时间排序(倒序)
        List<User> resultList = list.stream().sorted(Comparator.comparing(User::getCreateTime).reversed()).collect(Collectors.toList());
        
        resultList.forEach(System.err::println);
    }

执行结果:
在这里插入图片描述

4.3 对中文进行排序

此处用了 hutool拼音工具 获取中文拼音,具体使用方法可点击查看官网介绍。
【示例】

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 根据用户名称排序(正序)
//        List<User> resultList = list.stream().sorted(Comparator.comparing(User::getUserName, (x, y) -> {
//            x = PinyinUtil.getPinyin(x);
//            y = PinyinUtil.getPinyin(y);
//            Collator instance = Collator.getInstance();
//            return instance.compare(x, y);
//        })).collect(Collectors.toList());

        // 根据用户名称排序(倒序)
        List<User> resultList = list.stream().sorted(Comparator.comparing(User::getUserName, (x, y) -> {
            x = PinyinUtil.getPinyin(x);
            y = PinyinUtil.getPinyin(y);
            Collator instance = Collator.getInstance();
            return instance.compare(x, y);
        }).reversed()).collect(Collectors.toList());

        resultList.forEach(System.err::println);
    }

执行结果:
在这里插入图片描述

8、分组方法

分组这里的测试数据不太好,重新造测试数据如下:
在这里插入图片描述

5.1 普通分组

【示例】根据年龄分组

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();
        
        // 根据年龄分组
        Map<Integer, List<User>> map = list.stream().collect(Collectors.groupingBy(User::getAge));

        map.forEach((key, value) -> {
            System.err.println("年龄为:"+ key + " 的数据集合:");
            value.forEach(System.err::println);
            System.err.println("--------------------------------------------------------------------------");
        });
    }

执行结果;
在这里插入图片描述

5.2 多级分组

【示例】先根据姓名分组,然后根据年龄分组

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();
        
        // 根据姓名分组,然后根据年龄分组
        Map<String, Map<Integer, List<User>>> map = list.stream().collect(Collectors.groupingBy(User::getUserName,
                Collectors.groupingBy(User::getAge)));

        map.forEach((key1, heightMap) -> {
            System.err.println(key1 + ":");
            heightMap.forEach((key2,user)-> {
                System.err.println(key2 + ":");
                user.forEach(System.err::println);
            });
            System.err.println("--------------------------------------------------------------------------");
        });
    }

执行结果:
在这里插入图片描述

5.3 分组后处理数据

下面的示例有些是我在写一些报表需求时候写的,暂时不理解没关系,遇到相应的需求时就会理解了。
【示例】根据名称分组,然后计算平均年龄

public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 根据名称分组,然后计算平均年龄
        Map<String, Double> map = list.stream().collect(Collectors.groupingBy(User::getUserName,
                Collectors.averagingInt(User::getAge)));

        map.forEach((key, value) -> {
            System.err.println(key + "的平均年龄:" + value);
        });
    }

执行结果:
在这里插入图片描述
【示例】根据姓名分组,然后去年龄最大的

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 根据姓名分组,然后去年龄最大的
        Map<String, Integer> map = list.stream().collect(Collectors.groupingBy(User::getUserName,
                Collectors.collectingAndThen(Collectors.toList(),
                        l -> l.stream().map(User::getAge).reduce(Integer::max).get())));

        map.forEach((k, v) -> {
            System.err.println("key: " + k + "-----" + "value: 最大年龄为:" + v);
        });
        
    }

执行结果:
在这里插入图片描述
【示例】先根据姓名分组,然后根据年龄分组并取第一条的身高

	public static void main(String[] args) {
        // 获取测试数据
        List<User> list = getUserList();

        // 先根据姓名分组,然后根据年龄分组并取第一条的身高
        Map<String, Map<Integer, BigDecimal>> map = list.stream().collect(
                Collectors.groupingBy(User::getUserName,
                        Collectors.groupingBy(User::getAge,
                                Collectors.mapping(User::getHeight,
                                        Collectors.collectingAndThen(Collectors.toList(), l -> l.get(0))))));

        map.forEach((key1, ageMap) -> {
            System.err.println(key1 + ":");
            ageMap.forEach((key2, height)-> {
                System.err.println(key2 + ":");
                System.err.println("身高:" + height);
            });
            System.err.println("--------------------------------------------------------------------------");
        });
    }

执行结果:
在这里插入图片描述

9、对map的简单操作

下列示例在处理字典数据的时候会用到。
【示例】转换map集合的数据类型

	public static void main(String[] args) {
        // 获取测试数据
        Map<Object, Object> map = new HashMap<>();
        map.put(1, 200.3);
        map.put(2, 500);

        Map<String, String> stringMap = map.entrySet().stream().collect(Collectors.toMap(k -> k.getKey().toString(), v -> v.getValue().toString()));

        stringMap.forEach((k, v) -> {
            System.err.println("key:" + k + "---------" + "value:" + v);
        });
        
    }

执行结果:
在这里插入图片描述
【示例】反转map的key、value

	 public static void main(String[] args) {
        // 获取测试数据
        Map<Object, Object> map = new HashMap<>();
        map.put(1, "格斗家");
        map.put(2, "鬼剑士");

        Map<String, String> reverseMap = map.entrySet().stream().collect(Collectors.toMap(k -> k.getValue().toString(), v -> v.getKey().toString()));

        reverseMap.forEach((k, v) -> {
            System.err.println("key:" + k + "---------" + "value:" + v);
        });
    }

执行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值