BY Jason
Java18都发布了,8也发布了十年了,你不会还在用for循环遍历集合吧?
本篇文章将对Stream流常用的方法进行介绍,主要为遍历(map
,peek
)、过滤(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;
}
List<User> userList = new ArrayList<>();
userList.add(reba);
userList.add(nazha);
userList.add(feng);
// 本质是将一个数组转成list,数组的大小是固定的,所以此list不能添加元素
// 如果调用add方法增加新的元素,会报异常:java.lang.UnsupportedOperationException
List<User> userList = Arrays.asList(reba, nazha, feng);
// stream流,创建的是动态数组,可以添加元素
List<User> userList = Stream.of(reba, nazha, feng).collect(Collectors.toList());
用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
List<User> collect = userMap.entrySet().stream()
.map(Map.Entry::getValue)
.collect(Collectors.toList());
总结:流的返回结果固定是Map,key为分组的值,valve为分组后流的输出结果,分组只能在collect中使用。
Map<Integer, List<User>> collect1 = list.stream()
// 按照年龄分组,分组后的流转List集合
.collect(Collectors.groupingBy(User::getAge, Collectors.toList()));
总结:map可以返回一个新元素类型的流,可以改变原来集合流的泛型类型,可以在需要改变泛型类型的时候使用。
List<Woman> collect = userList.stream()
// map遍历后可以改变集合的泛型类型
// 直接返回转换后的对象即可
.map(user -> Woman.builder()
.username(user.getName())
.age(user.getAge())
.build())
.collect(Collectors.toList());
总结:peek只能返回一个当前元素类型的流,不能改变原集合流的泛型类型。
List<User> girlList = userList.stream()
// 无返回值:不改变集合泛型类型
.peek(user -> {
user.setSex("女");
})
.collect(Collectors.toList());
总结:跟peek相似,foreach同样不能改变集合泛型类型,也不会继续返回stream流,不建议使用,直接【集合.foreach】即可,本质都是迭代器,foreach后就不是流了。
// ========== 不建议使用 =====================
userList.stream().forEach(
user -> {
user.setSex("女");
}
);// 不会继续返回流,不能再对流进行操作。
// ========== 使用此方法遍历集合 ==============
userList.forEach(
user -> user.setSex("女")
);
需要改变原集合泛型类型时,使用map
;
不需要改变原集合泛型类型,并且还需要继续流操作时,使用peek
;
不需要改变原集合泛型类型,并且不需要流操作时,直接使用迭代器forEach
;
总结:lambda断言函数接口。返回值为true,则保留元素;返回值为false,移除掉元素。
List<User> users = userList.stream()
// 返回年龄小于60的用户
.filter(user -> user.getAge() < 60)
.collect(Collectors.toList());
总结:比较函数接口 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流操作、递归操作,将数据库查询出的菜单集合转树形结构。
/**
* 普通菜单集合转成树形结构 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 目标的泛型裂隙
* @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 目标的泛型裂隙
* @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 目标的泛型裂隙
* @return 转换后的集合
*/
public static <T> Set<T> copyWithCollection(Set<?> sourceList, Class<T> target) {
return sourceList.stream().map(s -> copyProperties(s, target)).collect(Collectors.toSet());
}
}