Java 8 Stream API的使用

一、什么是Stream?

Stream(流)是一个来自数据源的元素队列,它可以支持聚合操作。

  • 数据源:流的数据来源,构造Stream对象的数据源,比如通过一个List来构造Stream对象,这个List就是数据源;
  • 聚合操作:对Stream对象进行处理后使得Stream对象返回指定规则数据的操作称之为聚合操作,比如filter、map、limit、sorted等都是聚合操作。

* Stream 与 集合对比

二、Stream 聚合操作

UmsMenu是一个菜单对象,具有树形结构,对象定义如下。

public class UmsMenu implements Serializable {
    private Long id;

    @ApiModelProperty(value = "父级ID")
    private Long parentId;

    @ApiModelProperty(value = "创建时间")
    private Date createTime;

    @ApiModelProperty(value = "菜单名称")
    private String title;

    @ApiModelProperty(value = "菜单级数")
    private Integer level;

    @ApiModelProperty(value = "菜单排序")
    private Integer sort;

    @ApiModelProperty(value = "前端名称")
    private String name;

    @ApiModelProperty(value = "前端图标")
    private String icon;

    @ApiModelProperty(value = "前端隐藏")
    private Integer hidden;

    //省略所有getter及setter方法
}

三、Stream对象的创建

Stream对象分为两种,一种串行的流对象,一种并行的流对象。

// menuList指所有菜单列表
// 为集合创建串行流对象
Stream<UmsMenu> stream = menuList.stream();
// 为集合创建并行流对象
Stream<UmsMenu> parallelStream = menuList.parallelStream();

四、filter

对Stream中的元素进行过滤操作,当设置条件返回true时返回相应元素。

@Test
public void filterTest(){
    //filter操作:获取所有一级菜单
    List<UmsMenu> oneLevelList = menuList.stream()
    .filter(menu -> menu.getParentId() == 0L)
    .collect(Collectors.toList());
    LOGGER.info("filter操作:{}",oneLevelList);
}

五、map

        对Stream中的元素进行转换处理后获取,比如可以将UmsMenu对象转换成Long对象。我们经常会有这样的需求:需要把某些对象的id提取出来,然后根据这些id去查询其他对象,这时可以使用此方法。

@Test
public void mapTest(){
    //map操作:获取所有菜单的id
    List<Long> idList = menuList.stream()
    .map(menu -> menu.getId())
    .collect(Collectors.toList());
    LOGGER.info("map操作:{}",idList);
}

六、limit

从Stream中获取指定数量的元素。

@Test
public void limitTest(){
    //limit操作:获取前5个菜单
    List<UmsMenu> firstFiveList = menuList.stream()
            .limit(5)
            .collect(Collectors.toList());
    LOGGER.info("limit操作:{}",firstFiveList);
}

七、count

仅获取Stream中元素的个数。

@Test
public void countTest(){
    //count操作:获取所有一级菜单的个数
    long count = menuList.stream()
    .filter(menu -> menu.getParentId() == 0L)
    .count();
    LOGGER.info("count操作:{}",count);
}

八、sorted

对Stream中元素按指定规则进行排序。

@Test
public void sortedTest(){
    //sorted操作:将所有菜单按照sort字段进行排序
    List<UmsMenu> sortedList = menuList.stream()
            .sorted((menu1,menu2)->{return menu2.getSort().compareTo(menu1.getSort());})
            .collect(Collectors.toList());
    LOGGER.info("sorted操作:{}",sortedList);
}

九、skip

跳过指定个数的Stream中元素,获取后面的元素。

@Test
public void skipTest(){
    //skip操作:跳过前5个元素,返回后面的
    List<UmsMenu> skipList = menuList.stream()
            .skip(5)
            .collect(Collectors.toList());
    LOGGER.info("skip操作:{}",skipList);
}

十、应用

        我们经常会有返回树形结构数据的需求。比如这里的菜单,具有一级菜单、二级菜单、三级菜单这样的结构。如果我们要返回一个集合,包含菜单以及菜单下面嵌套的子菜单,使用Stream API可以很方便的解决这个问题。

        注意:这里我们的菜单上下级之间以parentId来关联,parentId是指上一级菜单的id,顶级菜单的parentId为0。

定义包含下级菜单的对象

继承自UmsMenu对象,只增加了一个children属性,用于存储下级菜单。

/**
 * @description 后台菜单节点封装
 */
@Getter
@Setter
public class UmsMenuNode extends UmsMenu {
    @ApiModelProperty(value = "子级菜单")
    private List<UmsMenuNode> children;
}

定义获取树形结构的方法

        我们先过滤出parentId为0的顶级菜单,然后给每个顶级菜单设置其子级菜单,covertMenuNode方法的主要用途就是从所有菜单中找出相应子级菜单。

@Override
public List<UmsMenuNode> treeList() {
    List<UmsMenu> menuList = menuMapper.selectByExample(new UmsMenuExample());
    List<UmsMenuNode> result = menuList.stream()
    .filter(menu -> menu.getParentId().equals(0L))
    .map(menu -> covertMenuNode(menu, menuList)).collect(Collectors.toList());
    return result;
}

为每个菜单设置子级菜单

        这里我们使用filter操作来过滤出每个菜单的子级菜单,由于子级菜单下面可能还会有子级菜单,这里我们使用递归来解决。但是递归操作什么时候停止,这里把递归调用方法放到了map操作中去,当没有子级菜单时filter下的map操作便不会再执行,从而停止递归。

/**
 * 将UmsMenu转化为UmsMenuNode并设置children属性
 */
private UmsMenuNode covertMenuNode(UmsMenu menu, List<UmsMenu> menuList) {
    UmsMenuNode node = new UmsMenuNode();
    BeanUtils.copyProperties(menu, node);
    List<UmsMenuNode> children = menuList.stream()
            .filter(subMenu -> subMenu.getParentId().equals(menu.getId()))
            .map(subMenu -> covertMenuNode(subMenu, menuList)).collect(Collectors.toList());
    node.setChildren(children);
    return node;
}

 用collect方法将List转成map

有时候我们需要反复对List中的对象根据id进行查询,我们可以先把该List转换为以id为key的map结构,然后再通过map.get(id)来获取对象,这样比较方便。

@Test
public void collect2mapTest(){
    //collect转map操作:将菜单列表以id为key,以菜单对象为值转换成map
    Map<Long, UmsMenu> menuMap = menuList.stream()
            .collect(Collectors.toMap(menu -> menu.getId(), menu -> menu));
    LOGGER.info("collect转map操作:{}",menuMap);
}

 数学运算

    public void testNumberCalculate() {
        List<Integer> ids = Arrays.asList(10, 20, 30, 40, 50);
        // 计算平均值
        Double average = ids.stream().collect(Collectors.averagingInt(value -> value));
        System.out.println("平均值:" + average);
        // 数据统计信息
        IntSummaryStatistics summary = ids.stream().collect(Collectors.summarizingInt(value -> value));
        System.out.println("数据统计信息: " + summary);
}

生成拼接字符串

    public void testCollectJoinStrings() {
        List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
        String joinResult = ids.stream().collect(Collectors.joining(","));
        System.out.println("拼接后:" + joinResult);
}

生成集合

    public void testCollectStopOptions() {
        List<User> ids = Arrays.asList(new User(17), new User(22), new User(23));
        // collect成list
        List<User> collectList = ids.stream().filter(dept -> dept.getId() > 20)
                .collect(Collectors.toList());
        System.out.println("collectList:" + collectList);
        // collect成Set
        Set<User> collectSet = ids.stream().filter(dept -> dept.getId() > 20)
                .collect(Collectors.toSet());
        System.out.println("collectSet:" + collectSet);
        // collect成HashMap,key为id,value为Dept对象
        Map<Integer, User> collectMap = ids.stream().filter(dept -> dept.getId() > 20)
                .collect(Collectors.toMap(User::getId, dept -> dept));
        System.out.println("collectMap:" + collectMap);
    }

一旦一个Stream被执行了终止操作之后,后续便不可以再读这个流执行其他的操作了,否则会报错

    public void testHandleStreamAfterClosed() {
        List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
        Stream<String> stream = ids.stream().filter(s -> s.length() > 2);
        // 统计stream操作后剩余的元素个数
        System.out.println(stream.count());
        System.out.println("-----下面会报错-----");
        // 判断是否有元素值等于205
        try {
            System.out.println(stream.anyMatch("205"::equals));
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("-----上面会报错-----");
    }

 简单结果终止方法

    public void testSimpleStopOptions() {
        List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
        // 统计stream操作后剩余的元素个数
        System.out.println(ids.stream().filter(s -> s.length() > 2).count());
        // 判断是否有元素值等于205
        System.out.println(ids.stream().filter(s -> s.length() > 2).anyMatch("205"::equals));
        // findFirst操作
        ids.stream().filter(s -> s.length() > 2)
                .findFirst()
                .ifPresent(s -> System.out.println("findFirst:" + s));
    }

filter、sorted、distinct、limit

    public void testGetTargetUsers() {
        List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
        // 使用流操作
        List<User> results = ids.stream()
                .filter(s -> s.length() > 2)
                .distinct()
                .map(Integer::valueOf)
                .sorted(Comparator.comparingInt(o -> o))
                .limit(3)
                .map(id -> new User(id))
                .collect(Collectors.toList());
        System.out.println(results);
    }

演示map的用途:一对多转换

    public void stringToIntFlatmap() {
        List<String> sentences = Arrays.asList("hello world", "Jia Gou Wu Dao");
        // 使用流操作
        List<String> results = sentences.stream()
                .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
                .collect(Collectors.toList());
        System.out.println(results);
    }

 

演示map的用途:一对一转换

    public void stringToIntMap() {
        List<String> ids = Arrays.asList("205", "105", "308", "469", "627", "193", "111");
        // 使用流操作
        List<User> results = ids.stream()
                .map(id -> {
                    User user = new User(Integer.valueOf(id));
                    return user;
                })
                .collect(Collectors.toList());
        System.out.println(results);
    }

补充:Java 8 新特性

JDK的前世今生:细数 Java5 - 15 的那些经典特性

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值