JDK8 常用的Stream流操作 方法

原文地址

BY Jason

Stream流

Java18都发布了,8也发布了十年了,你不会还在用for循环遍历集合吧?

本篇文章将对Stream流常用的方法进行介绍,主要为遍历(mappeek)、过滤(filter)、排序(sort)、集合转换(collect)。

Stream流map方法、Stream流排序、Stream流map和peek的区别、Stream流过滤filter…

    private static List<User> getUserList() {
        User reba = User.builder().age(26).name("迪丽热巴").build();
        User nazha = User.builder().age(26).name("古力娜扎").build();
        User feng = User.builder().age(65).name("罗玉凤").build();

        // stream快速创建集合 .collect()
        List<User> list = Stream.of(reba, nazha, feng).collect(Collectors.toList());
        Set<User> set = Stream.of(reba, nazha, feng).collect(Collectors.toSet());
        return list;
    }

Stream创建集合

普通List创建集合

List<User> userList = new ArrayList<>();
userList.add(reba);
userList.add(nazha);
userList.add(feng);

Arrays创建集合

// 本质是将一个数组转成list,数组的大小是固定的,所以此list不能添加元素
// 如果调用add方法增加新的元素,会报异常:java.lang.UnsupportedOperationException
List<User> userList = Arrays.asList(reba, nazha, feng);

Stream创建集合

// stream流,创建的是动态数组,可以添加元素
List<User> userList = Stream.of(reba, nazha, feng).collect(Collectors.toList());

Stream流转集合(collect)

List转Map

用Collectors的toMap方法转换List,一般会遇到两个问题:

a. 转换map,key重复问题:

代码中使用(key1,key2)->key2表达式可以解决此类问题,如果出现重复的key就使用key2覆盖前面的key1,也可以定义成(key1,key2)->key1,保留key1,根据自己的业务场景来调整。

b. 空指针异常,即转为map的value是null。这个可以先用filter过滤;

		// stream list转map
        Map<String, User> userMap = list.stream()
                .collect(Collectors.toMap(
                        // map key的值
                        User::getName,
                        // map value的值
                        user -> user,
                        // key重复保留后面的key
                        (key1, key2) -> key2)
                );

Map转List

本质就是遍历产生新集合

 		// map转list
        List<User> collect = userMap.entrySet().stream()
            .map(Map.Entry::getValue)
            .collect(Collectors.toList());

groupingBy分组

总结:流的返回结果固定是Map,key为分组的值,valve为分组后流的输出结果,分组只能在collect中使用。

		Map<Integer, List<User>> collect1 = list.stream()
            	// 按照年龄分组,分组后的流转List集合
                .collect(Collectors.groupingBy(User::getAge, Collectors.toList()));

Stream遍历(map、peek、forEach)

map

总结:map可以返回一个新元素类型的流,可以改变原来集合流的泛型类型,可以在需要改变泛型类型的时候使用。

        List<Woman> collect = userList.stream()
                // map遍历后可以改变集合的泛型类型
                // 直接返回转换后的对象即可
                .map(user -> Woman.builder()
                        .username(user.getName())
                        .age(user.getAge())
                        .build())
                .collect(Collectors.toList());

peek

总结:peek只能返回一个当前元素类型的流,不能改变原集合流的泛型类型。

		List<User> girlList = userList.stream()
                // 无返回值:不改变集合泛型类型
                .peek(user -> {
                    user.setSex("女");
                })
                .collect(Collectors.toList());

forEach【迭代器】

总结:跟peek相似,foreach同样不能改变集合泛型类型,也不会继续返回stream流,不建议使用,直接【集合.foreach】即可,本质都是迭代器,foreach后就不是流了。

        // ========== 不建议使用 =====================
        userList.stream().forEach(
                user -> {
                    user.setSex("女");
                }
        );// 不会继续返回流,不能再对流进行操作。
        // ========== 使用此方法遍历集合 ==============
        userList.forEach(
                user -> user.setSex("女")
        );

总结

  • 需要改变原集合泛型类型时,使用map

  • 不需要改变原集合泛型类型,并且还需要继续流操作时,使用peek

  • 不需要改变原集合泛型类型,并且不需要流操作时,直接使用迭代器forEach

Stream过滤(filter)

总结:lambda断言函数接口。返回值为true,则保留元素;返回值为false,移除掉元素。

		List<User> users = userList.stream()
                // 返回年龄小于60的用户
                .filter(user -> user.getAge() < 60)
                .collect(Collectors.toList());

Stream排序(sort)

总结:比较函数接口 o1代表o2前一个元素,大于0(1)交换两元素位置,即:【o1 - o2:升序】 【o2 - o1:降序】

完整lambda写法:升序

        List<User> collect = userList.stream()
                // 完整lambda表达式写法 按照年龄升序(o1 - o2) 
                .sorted(((o1, o2) -> {
                    return o1.getAge() - o2.getAge();
                })).collect(Collectors.toList());

省略lambda写法:降序

        collect = userList.stream()
                // 省略lambda表达式写法 按照年龄降序(o2 - o1)
                .sorted((o1, o2) ->  o2.getAge() - o1.getAge())
                .collect(Collectors.toList());

升序简便写法:

        collect = userList.stream()
                // 升序简便写法(返回需要按照升序排序的树形值即可)
                .sorted((Comparator.comparingInt(User::getAge)))
                .collect(Collectors.toList());

Stream使用案例

菜单转树形结构

可以通过Stream流操作、递归操作,将数据库查询出的菜单集合转树形结构。

	/**
     * 普通菜单集合转成树形结构 ParentId为单父菜单ID 其中ParentId=0时为一级菜单
     * 1. 获取一级菜单,再遍历递归封装子菜单
     * 2. 一级菜单的个数即为树形集合的长度
     *
     * @param entities 普通菜单集合
     * @return 树形菜单集合
     */
    private List<MenuDTO> menusToTree(List<MenuDTO> entities) {
        // 组装成父子的树形结构
        return menuDTOS.stream()
                .filter(menu ->
                        // 获取一级菜单
                        menu.getParentId() == 0
                )
                // 遍历封装子菜单
                .peek(menu -> {
                    // 获取子菜单
                    menu.setChildren(getChildren(menu, menuDTOS));
                })
                // 一级菜单升序排序 sort越大越靠后
                .sorted(Comparator.comparingInt(MenuDTO::getSort))
                .collect(Collectors.toList());
    }

	/**
     * 获取子菜单集合
     *
     * @param root 父菜单
     * @param all  所有菜单集合
     * @return root的子菜单集合
     */
    private List<MenuDTO> getChildren(MenuDTO root, List<MenuDTO> all) {
        return all.stream()
                .filter(menu -> {
                    // 筛选出子菜单(父菜单ID = 子菜单父ID)
                    return menu.getParentId().equals(root.getId());
                })
                .peek(menu -> {
                    // 递归筛选子菜单
                    menu.setChildren(getChildren(menu, all));
                })
                // return 1-2,升序   return 2-1 降序 (结果为正交换位置)
                .sorted((menu1, menu2) -> {
                    return menu1.getSort() - menu2.getSort();
                }).collect(Collectors.toList());
    }

集合泛型类型转换

package com.feizhaiyou.blog.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 对象拷贝
 *
 * @author Jason
 */
@Slf4j
public class BeanHelper {

    /**
     * 拷贝对象
     *
     * @param source 源对象
     * @param target 目标对象的类型
     * @param <T>    目标的泛型裂隙
     * @return 目标类型的对象
     */
    public static <T> T copyProperties(Object source, Class<T> target) {
        try {
            T t = target.newInstance();
            BeanUtils.copyProperties(source, t);
            return t;
        } catch (InstantiationException e) {
            throw new RuntimeException(target.getName() + "无法被实例化,可能是一个接口或抽象类");
        } catch (IllegalAccessException e) {
            throw new RuntimeException(target.getName() + "无法被实例化,构造函数无法访问");
        }
    }

    /**
     * 拷贝集合List
     *
     * @param sourceList 源集合
     * @param target     目标类型
     * @param <T>        目标的泛型裂隙
     * @return 转换后的集合
     */
    public static <T> List<T> copyWithCollection(List<?> sourceList, Class<T> target) {
        return sourceList.stream()
            // 遍历集合返回新类型对象
            .map(s -> copyProperties(s, target))
            .collect(Collectors.toList());
    }

    /**
     * 拷贝集合Set
     *
     * @param sourceList 源集合
     * @param target     目标类型
     * @param <T>        目标的泛型裂隙
     * @return 转换后的集合
     */
    public static <T> Set<T> copyWithCollection(Set<?> sourceList, Class<T> target) {
        return sourceList.stream().map(s -> copyProperties(s, target)).collect(Collectors.toSet());
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Stream是在Java 8之后新增的一个特性,它是用来操作集合(Collection)的工具。Stream提供了一种简洁、高效的方式来对集合进行处理和操作Stream可以用于遍历集合中的元素、进行过滤、映射、排序、聚合等各种操作。 在使用Stream时,常用方法之一是forEach()方法,它用来遍历Stream中的数据。这个方法是一个终结方法,一旦调用了forEach()方法,就不能再继续调用Stream中的其他方法了。例如,下面的代码演示了如何使用forEach()方法遍历一个Stream并打印其中的元素: ``` List<String> list = Arrays.asList("字节","网易","百度", "美团", "阿里", "字节"); Stream<String> stream = list.stream(); stream.forEach(System.out::println); ``` 执行以上代码会依次打印出集合中的元素:"字节"、"网易"、"百度"、"美团"、"阿里"、"字节"。 除了forEach()方法之外,Stream还提供了许多其他常用方法,比如filter()用于过滤元素、map()用于映射元素、sorted()用于排序元素、reduce()用于聚合元素等等。这些方法可以根据具体的需求来选择使用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [JDK1.8常见的Stream](https://blog.csdn.net/TFHoney/article/details/129687717)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥宅友

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值