使用stream流处理常见的业务场景

使用stream流处理常见的业务场景

我整理了一些常见的业务逻辑场景的处理,于是写下这篇博客来方便自己回忆。

熟练使用Ctrl+C,Ctrl+V,Ctrl+F,Ctrl+R体验更佳哦

测试数据

  • 整数列表
List<Integer> integerList = new ArrayList<>(Arrays.asList(7,3,5,5,6,0,8));
  • 字符串列表
List<String> stringList = new ArrayList<>(Arrays.asList("","apple","orange","banana","Peach","watermelon"));
  • 对象列表
List<Menu> menuList = new ArrayList<>(Arrays.asList(new Menu(1L,"系统管理",0L),
                                                    new Menu(1001L,"用户查询",100L),
                                                    new Menu(1002L,"用户新增",100L),
                                                    new Menu(1003L,"用户修改",100L),
                                                    new Menu(1004L,"用户删除",100L),
                                                    new Menu(2L,"仪表盘",0L),
                                                    new Menu(100L,"用户管理",1L),
                                                    new Menu(1008L,"角色查询",101L),
                                                    new Menu(1009L,"角色新增",101L),
                                                    new Menu(1014L,"菜单新增",102L),
                                                    new Menu(1015L,"菜单修改",102L),
                                                    new Menu(1010L,"角色修改",101L),
                                                    new Menu(1011L,"角色删除",101L),
                                                    new Menu(101L,"角色管理",1L),
                                                    new Menu(1005L,"用户导出",100L),
                                                    new Menu(1006L,"用户导入",100L),
                                                    new Menu(1007L,"重置密码",100L),
                                                    new Menu(102L,"菜单管理",1L),
                                                    new Menu(1012L,"角色导出",101L),
                                                    new Menu(1013L,"菜单查询",102L)));
class Menu{
    Long id;
    String menuName;
    Long parentId;
    List<Menu> children;

    Menu(Long id,String menuName,Long parentId){
        this.id = id;
        this.menuName = menuName;
        this.parentId = parentId;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getMenuName() {
        return menuName;
    }

    public void setMenuName(String menuName) {
        this.menuName = menuName;
    }

    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    public List<Menu> getChildren() {
        return children;
    }

    public Menu setChildren(List<Menu> children) {
        this.children = children;
        return this;
    }

    @Override
    public String toString() {
        return "Menu{" +
                "id=" + id +
                ", menuName='" + menuName + '\'' +
                ", parentId=" + parentId +
                ", children=" + children +
                '}';
    }
}

场景一:遍历

这个很简单了,无论代码长短,用你熟悉的就行了。

  • 使用foreach遍历(注意:不要在 foreach 循环里进行该列表元素的 remove/add 操作)
stringList.forEach(System.out::println);
//menuList.forEach(o -> System.out.println(o.toString()));
  • 使用迭代器遍历(注意:如果并发操作,需要对 Iterator 对象加锁)
Iterator<String> iterator = stringList.iterator();
while (iterator.hasNext()){
    String str = iterator.next();
    System.out.println(str);
}
  • 使用增强for循环
for(String s:stringList){
    System.out.println(s);
}

场景二:排序

排序往往要配合一些条件来操作列表,比如过滤null值或自定义条件,map处理等,这里只做排序

  • 注意:

Comparator 实现类要满足如下三个条件

1) x,y 的比较结果和 y,x 的比较结果相反

2) x>yy>z,则 x>z

3) x=y,则 x,z 比较结果和 y,z 比较结果相同

不然 Arrays.sort,Collections.sort 会抛 IllegalArgumentException 异常

  • 使用Collections的sort方法进行自定义排序(原地排序)
Collections.sort(stringList, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        // 自定义排序逻辑
        return o1.compareTo(o2);
    }
});
//Collections.sort(stringList,(s1,s2) -> s1.compareTo(s2));
  • 使用stream流的sorted方法进行自定义排序(产生新列表)
List<Menu> collect = menuList.stream()
                .sorted(Comparator.comparing(Menu::getId))
                .collect(Collectors.toList());

场景三:过滤元素/map元素

这个场景不太好举例子,因为业务逻辑太复杂了,最好配合api文档或者多查查大佬的博客来写代码,这里介绍一点简单的逻辑

  • stream流的filter方法
// 注意:多重filter中,下一层filter会从上一层filter处理完的结果集中进行处理,这里的filter可以理解为“保留..”
List<String> collect = stringList.stream()
                .filter(s -> !s.isEmpty())
                .filter(s -> s.split("^[a|b]").length>1)
                .collect(Collectors.toList());
  • stream流的map方法
// map方法会将元素处理成另一种你想要的数据类型,这里就获得了menuList里面所有的menuName的长度列表
List<Integer> collect = menuList.stream()
                .filter(Objects::nonNull)
                .map(o -> o.menuName.length())
                .collect(Collectors.toList());
  • list转map(Collectors的toMap方法)
Map<Long, String> collect = menuList.stream().collect(Collectors.toMap(Menu::getId, Menu::getMenuName));

场景四:分组

这里根据menu对象的id长度分组

  • Collectors的groupingBy方法(分组逻辑得自己写)
Map<Integer, List<Menu>> collectMap = menuList.stream()
                .filter(Objects::nonNull)
    			.filter(o->o.id!=null)
                .collect(Collectors.groupingBy(o -> o.id.toString().length()));

场景五:去重

去重可没有模板,往往得根据实际需求来操作,我经常在实现去重的时候使用哈希表来优化查询,提高速度

  • 直接使用stream流里面的distinct方法(去重方式是调用了equals()方法进行验证)
List<Menu> collect = menuList.stream()
                .distinct()
                .collect(Collectors.toList());
  • 自定义去重字段(根据menuName去重)
Map<String,Boolean> hashmap = new HashMap<>(16);
List<Menu> collect = menuList.stream()
                .filter(Objects::nonNull)
                .filter(o -> hashmap.get(o.menuName)==null)
                .peek(o -> hashmap.putIfAbsent(o.getMenuName(), true))
                .collect(Collectors.toList());

根据多个字段去重的思路也差不多,把这些字段转换成字符串然后拼接起来作为key就行了。

场景六:获取树形/文件夹形列表

比如一些权限管理,菜单管理后端返回前端的数据就是树形列表

public static List<Menu> builderMenuTree(List<Menu> menus, Long parentId) {
    return menus.stream()
        .filter(menu -> menu.getParentId().equals(parentId))
        .map(menu -> menu.setChildren(getChildren(menu, menus)))
        .collect(Collectors.toList());
}
public static List<Menu> getChildren(Menu menu, List<Menu> menus) {
    return menus.stream()
        .filter(m -> m.getParentId().equals(menu.getId()))
        .map(m -> m.setChildren(getChildren(m,menus)))
        .collect(Collectors.toList());
}
  • 获取树形列表
// 注意!这里的0代表根节点的parentId
List<Menu> treeMenu = builderMenuTree(menuList,0L);

debug图片

场景七:查询/匹配元素

  • stream()的allMatch方法:是否所有元素匹配成功
boolean b = menuList.stream().allMatch(o -> o.menuName.isEmpty());// 条件随便写的,主要是熟悉一下api,下同
  • stream()的anyMatch方法:是否任意一个元素匹配成功
boolean b = menuList.stream().anyMatch(o -> o.menuName.isEmpty());
  • stream().noneMatch:是否没有一个元素匹配成功
boolean b = menuList.stream().noneMatch(o -> o.menuName.isEmpty());
  • 正则表达式
略,太多了自己查

场景八:统计

这里只简单介绍一下api,数值统计要根据实际场景,可能有的场景还有数学公式

IntSummaryStatistics collect = integerList.stream()
                .collect(Collectors.summarizingInt(o -> o));
long sum = collect.getSum();
int max = collect.getMax();
int min = collect.getMin();
long count = collect.getCount();
double average = collect.getAverage();
Long count = menuList.stream().filter(Objects::nonNull).count();
Double avg = menuList.stream().collect(Collectors.averagingLong(Menu::getId));
Optional<Menu> max = menuList.stream().max(Comparator.comparingLong(o -> o.id));
Optional<Menu> min = menuList.stream().min(Comparator.comparingLong(o -> o.id));
// BigDecimal累加求和
list.stream().map(obj -> new BigDecimal(obj.score())).reduce(BigDecimal.ZERO, BigDecimal::add);

场景九:拼接

  • 直接拼接
String collect = menuList.stream()
                .map(Menu::getMenuName)
                .collect(Collectors.joining());
  • 以间隔为逗号拼接
String collect = menuList.stream()
                .map(Menu::getMenuName)
                .collect(Collectors.joining(","));
  • 以间隔为逗号拼接,并设置开头与末尾的字符
String collect = menuList.stream()
                .map(Menu::getMenuName)
                .collect(Collectors.joining(",", "[", "]"));

场景十:字符串处理

这个其实只要查一下String相关的api然后配合stream的map方法就好了,比较麻烦的正则表达式和时间格式的匹配先跳过

等我再打磨打磨然后再写一篇博客

  • 全部转成大写字母
List<String> collect = stringList.stream()
                .filter(Objects::nonNull)
    			.filter(s -> !s.isEmpty())
                .map(String::toUpperCase)
                .collect(Collectors.toList());
  • 全部转成小写字母
List<String> collect = stringList.stream()
                .filter(Objects::nonNull)
    			.filter(s -> !s.isEmpty())
                .map(String::toLowerCase)
                .collect(Collectors.toList());
  • 首字母大写,其余小写
List<String> collect = stringList.stream()
                .filter(Objects::nonNull)
                .filter(s -> !s.isEmpty())
                .map(s -> {
                    char[] chars = s.toLowerCase().toCharArray();
                    chars[0]-=32;
                    return new String(chars);
                })
                .collect(Collectors.toList());

未完待续。。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

fan_11235813

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

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

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

打赏作者

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

抵扣说明:

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

余额充值