tips: 看标题可能很难理解在说什么,建议结合代码案例看 看了代码一下就能明白了
文章目录
- stream流部分
- 一、当list泛型是一个实体类,需要按照某一个字段进行排序时:
- 二、当list泛型是一个单类型的时候 排序(含基本类型):
- 三、求交集(list共同部分)
- 四、list转map (当list泛型是个实体类的时候 )
- 五、list转map (当list泛型是单类型 如基本类型的时候 )
- 六、 list给泛型某个属性统一赋值(stream流的for循环操作)
- 七、 将list泛型的某个属性提取出来 组成新的list
- 八、取出符合条件的数据到新的list
- 九、求两个list差集(list1-list2差集)
- 十、list更换泛型
- 十一、list 按某个字段分组
- 十二、list 按某个字段分组后相加聚合
- 十三、 求List < Entity > 中 某个字段值的总和
- 十四、 求List < Entity > 中 某个字段值的最大值 (方法比较多 例举三种)
- 十五、 String[] 转List < Long >
- 十六、 stream流如何处理嵌套循环 用flatMap代替for for循环
- 十七、 stream流收集成单个对象 (list.get(0))
- 十八、将两个list泛型中,某个字段值相等的数据提取出来
- 十九、创建stream对象
- 二十、stream流读取文件
- 二十一、stream流截取n个数
- 二十二、stream流跳过n个元素
- 二十三、stream流reduce规约
- 二十四、stream流匹配写法 allMatch、anyMatch 和 noneMatch
- optional部分
stream流部分
一、当list泛型是一个实体类,需要按照某一个字段进行排序时:
//Comparator.comparing(People::getAge)) 按照年龄排序
// .collect(Collectors.toList()); 将stream流反序列化成list
List<People> collectList= peopleList.stream()
.sorted(Comparator.comparing(People::getAge))
.collect(Collectors.toList());
// 倒序
List<People> collectList= peopleList.stream()
.sorted(Comparator.comparing(People::getAge).reversed())
.collect(Collectors.toList());
二、当list泛型是一个单类型的时候 排序(含基本类型):
// sorted() 里面不需要参数 例如:List<LocalDate>dateList = new ArrayList<LocalDate>(); 按照时间排序
List<LocalDate> collectList = list.stream()
.sorted().collect(Collectors.toList());
三、求交集(list共同部分)
List<java.time.LocalDate> resultList= list1.stream()
.filter(item -> list2.contains(item))
.collect(Collectors.toList());
四、list转map (当list泛型是个实体类的时候 )
List<People> list = new ArrayList<People>;
Map<String, Integer> collect1 = list .stream().collect(Collectors.toMap(People::getName,People::getAge));
五、list转map (当list泛型是单类型 如基本类型的时候 )
List<LocalDate> dateList = new ArrayList<>();
// p 为固定写法 这样就得到了一个key,value类型都一样的map
Map<LocalDate, LocalDate> collect1 = dateList.stream().collect(Collectors.toMap((p) -> p, (p) -> p));
六、 list给泛型某个属性统一赋值(stream流的for循环操作)
list.stream().forEach(item -> item.setXXX(value));
// 类似的 可以写匿名函数 , stream()也可以不要
list.stream().forEach(
item -> {
if(1==1){
item.setXxx(xxx);
}
}
)
七、 将list泛型的某个属性提取出来 组成新的list
List<People> list = new ArrayList();
List<String> newList= list .stream()
.map(People::getId).collect(Collectors.toList());
八、取出符合条件的数据到新的list
List<LineStation> lineStationList = xxx;
// filter : 过滤器
List<String> newList= lineStationList.stream().filter(item -> item.getLineId().equals("1")).collect(Collectors.toList());
九、求两个list差集(list1-list2差集)
// 解析:遍历取出每个list1中的元素,且不包含在list2中,重组成一个新的list
List<String> newList= list1.stream().filter(item -> !list2.contains(item)).collect(Collectors.toList());
十、list更换泛型
// demo 其实就是在匿名函数里面自己写逻辑
Set<PbcDepot> existPbcDepotSet = existPbcDepotList.stream().map(item -> {
PbcDepot pbcDepot = new PbcDepot();
pbcDepot.setPbcId(item.getPbcId());
pbcDepot.setSDepotId(item.getSDepotId());
pbcDepot.setADepotId(item.getADepotId());
return pbcDepot;
}).collect(Collectors.toSet());
基本类型快速更换泛型
简单list:
List<Long> goodListTemp = xxxx;
// String::valueOf 演变过程
// 1. item -> {return String.valueOf(item)}
//2.item -> String.valueOf(item)
List<String> goodList = goodListTemp.stream().map(
String::valueOf
).collect(Collectors.toList());
复杂list:
// 其中SubletVehicleSchemeVO里面的goodNo是Long类型
List<SubletVehicleSchemeVO> subletVehicleSchemes = SubletVehicleSchemeWrapper.WRAPPER.entity2Vo(subletVehicleSchemeTemp);
List<String> goodNoList = subletVehicleSchemes.stream().map(item -> String.valueOf(item.getGoodsNo())).collect(Collectors.toList());
十一、list 按某个字段分组
// groupingBy的那个字段的值 将会作为map的key , 分组的结果作为value
// 注意groupingBy的那个字段的所有值 不能有null
List<CompanyParameterVO> topNewList = new ArrayList<>();
Map<String, List<CompanyParameterVO>> resultMap =
topNewList.stream()
.collect(Collectors.groupingBy(CompanyParameterVO::getParameterBelong));
十二、list 按某个字段分组后相加聚合
// 举个不太恰当的场景:数据库中 存的时间窗口是日期维度 而统计是月份维度
/**
* (正确做法应是select DATE_FORMAT(create_time,'%Y-%m')month from t group by month)
* 在数据库层面将时间给处理掉 按照月份去排序 ,下面是为了演示stream流的用法:
* 可以先写sql: select count(*),create_time from t group by t 返回一个mapperList
* 再利用stream流的foreach :mapperList.stream(item -> {})
* item.setMonth(item.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM")));
* 此时得到的list结果示例: [{"month":2022-04,"count":1},{"month":2022-04,"count":1}]
* 我们需要将同一个month里面的count进行相加聚合
*
*/
// 注意groupingBy字段的值 不能有null值
Map<String, Integer> mapResult = mapperList.stream()
.collect(
Collectors.groupingBy(
MonthCountDTO::getMonth,
Collectors.summingInt(MonthCountDTO::getCount)
)
)
// 此时得到的是map 如果直接通过responseBody返回json格式 示例: {"2022-04":2}
// 也就是说 json原来的key没了 只剩vaule了 这基本不符合我们开发的规范
// 我们可以手动将聚合结果转成实体类 再转一次
List<MonthCountDTO> result = new ArrayList<>();
for (Map.Entry<String, Integer> entry : mapResult.entrySet()) {
MonthCountDTO monthCountDTO = new MonthCountDTO()
.setCount(entry.getValue())
.setMonth(entry.getKey());
result.add(monthCountDTO);
}
// map是无序的 如果业务中 是统计图那种接口 我们还需要将月份排序一下
result = result.stream()
.sorted(Comparator.comparing(MonthCountDTO::getMonth))
.collect(Collectors.toList());
十三、 求List < Entity > 中 某个字段值的总和
// Entity中含有long number 字段
Map<String,Entity> map = xxxxxxx;
Collection<ReservedAmountBo> values = map.values();
long total= values.stream().mapToLong(Entity::getNumber).sum();
十四、 求List < Entity > 中 某个字段值的最大值 (方法比较多 例举三种)
// Entity中含有long number 字段
long asLong = list.stream().mapToLong(People::getAge).max().getAsLong();
Long long1 = list.stream().map(People::getAge).reduce(Long::max).get();
// 如果是比较时间等字段的最大值 核心是max(Comparator.naturalOrder()) 示例
List<ZonedDateTime> ls = new ArrayList<>();
ls.add(ZonedDateTime.now().plusSeconds(1111));
ls.add(ZonedDateTime.now().plusSeconds(2222));
ls.add(ZonedDateTime.now().plusSeconds(3333));
ZonedDateTime zonedDateTime = ls.stream().max(Comparator.naturalOrder()).orElse(null);
十五、 String[] 转List < Long >
// 首先来段String[] arr 转 List<String>
List<String> strList = Arrays.stream(arr).collect(Collectors.toList());
// arr转List<Long>
List<Long> list = Arrays.stream(arr)
.map(item -> Long.parseLong(item.trim())).collect(Collectors.toList());
十六、 stream流如何处理嵌套循环 用flatMap代替for for循环
这里以flatMap为例 ,flatMap和map的区别在于 flatMap可以把多个集合数据扁平化,通俗点说就是把原来我们要for for循环的数据给摊平。
List<List<String>> res = new ArrayList<>();
List<String> listChild = new ArrayList<>();
listChild.add("111");
listChild.add("122");
List<String> listChild2 = new ArrayList<>();
listChild2.add("133");
listChild2.add("144");
res.add(listChild);
res.add(listChild2);
List<String> xx = res.stream().flatMap(
item -> item.stream().map(
each -> each.replace("1", "xx"))
).collect(Collectors.toList());
// [xxxxxx, xx22, xx33, xx44]
System.out.println(xx);
十七、 stream流收集成单个对象 (list.get(0))
如果我们使用 list.stream.filter(item -> null != item).collect(Collectors.toList()).get(0) 的方式,
首先得先判断list是否为empty 否则会出现下标越界 , 我们可以用stream流api的方式去获取:
List<Ademo> list = new ArrayList<>();
Ademo ademo = new Ademo();
ademo.setName("11");
Ademo ademo1 = new Ademo();
ademo1.setName("22");
list.add(ademo);
list.add(ademo1);
// findFirst 返回值是optional
Ademo res = list.stream().filter(item -> Objects.equals("11", item.getName())).findFirst().orElse(null);
十八、将两个list泛型中,某个字段值相等的数据提取出来
// 导入的原始数据
List<ApplyChannelExcelImportDTO> imports = threadLocalImports.get();
// 公司列表
List<SalesCompany> companyList = threadLocalExistCompany.get();
// 导入原始数据中 在公司列表中不存在的公司名
List<String> resTemp = imports.stream()
.map(ApplyChannelExcelImportDTO::getSaleCompanyName)
.filter(name -> companyList.stream().filter(each -> each != null && StringUtils.isNotBlank(each.getName()))
.noneMatch(each -> each.getName().equals(name)))
.collect(Collectors.toList());
尽管如此 复杂的逻辑 建议使用for循环 或者结合for使用 提高可读性 下面是for循环的写法
// 导入原始数据
List<ApplyChannelExcelImportDTO> imports = threadLocalImports.get();
// 公司列表
List<SalesCompany> companyList = threadLocalExistCompany.get();
// 导入成功数据
List<ApplyChannel> storageList = new ArrayList<>();
// 导入失败数据
List<ApplyChannelExcelImportFailDTO> failList = new ArrayList<>();
ApplyChannelExcelImportFailDTO eachFail;
for (ApplyChannelExcelImportDTO eachImport : imports) {
// 导入公司是否存在公司范围内
boolean existCompany = false;
// 导入数据是否存在渠道
boolean existApplyChannel = false;
for (SalesCompany salesCompany : companyList) {
if (Objects.equals(eachImport.getSaleCompanyName(), salesCompany.getName())) {
existCompany = true;
break;
}
}
if (StringUtils.isNotBlank(eachImport.getApplyChannel())) {
existApplyChannel = true;
}
// 如果导入数据中 公司不存在或渠道为空
if (!existCompany || !existApplyChannel) {
eachFail = new ApplyChannelExcelImportFailDTO();
eachFail.setSaleCompanyName(eachImport.getSaleCompanyName());
eachFail.setApplyChannel(eachImport.getApplyChannel());
if (!existCompany) {
eachFail.setMessage("公司不存在");
} else {
eachFail.setMessage("渠道为空");
}
failList.add(eachFail);
} else {
// 一个类型转换
ApplyChannel applyChannel = ApplyChannelWrapper.WRAPPER.dto2Entity(eachImport);
storageList.add(applyChannel);
}
}
十九、创建stream对象
tips: 创建stream对象后就可以进行一系列的stream流操作了
// 静态定义
Stream<Integer> stream = Stream.of(1, 2, 3);
// 动态定义 在需要添加元素时 add()
Stream.Builder<String> builder = Stream.builder();
builder.add("test");
Stream<String> stream1 = builder.build();
二十、stream流读取文件
Path path = Paths.get("C:\\Users\\xxx\\Desktop\\test.txt");
try {
// lines方法里面已经close()了 不必担心
Stream<String> lines = Files.lines(path);
List<String> lineStr = lines.collect(Collectors.toList());
System.out.println(lineStr);
} catch (IOException e) {
e.printStackTrace();
}
二十一、stream流截取n个数
Stream<Integer> stream = Stream.of(1,2,3);
// 截取前两个值 list : 1,2
List<Integer> list = stream.limit(2).collect(Collectors.toList());
二十二、stream流跳过n个元素
Stream<Integer> stream = Stream.of(1,2,3);
// list : 3
List<Integer> list = stream.skip(2).collect(Collectors.toList());
二十三、stream流reduce规约
reduce有点抽象 你不知道怎么去和别人解释 但是用起来是真的爽 博主尽可能用同学们能看懂的语言解释
我们直接看代码及其注释:
单个参数:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
// a+b 只是表达一个计算公式 ,表面明stream流里面的所有数字相加
Optional<Integer> reduce = stream.reduce((a, b) -> a + b);
// 15
Integer integer = reduce.get();
// 在这个简单的加法计算案例中,其实等效于下面代码:
// 注意:流计算完就关闭 需要再次创建
Stream<Integer> streamForEach = Stream.of(1, 2, 3, 4, 5);
AtomicReference<Integer> res = new AtomicReference<>(0);
streamForEach.forEach(
item ->{
// 相当于 int res += int each , 这里用了atomic 保证原子性
res.updateAndGet(v -> v + item);
}
);
// 15
Integer integer1 = res.get();
// 小tips 所有lambda表达式都可以改写成方法体形式:
Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5);
Optional<Integer> reduce = stream2.reduce((a, b) ->
{
System.out.println("一闪一闪亮晶晶");
// do something
return a + b;
});
两个参数:
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
// 第一个参数定义一个数 第二个参数定义方法 表示 额外一个数字10 参与stream流的累加
Integer add = stream1.reduce(10 , Math::addExact);
// 25
System.out.println(add);
// 注意:流计算完就关闭 需要再次创建
Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5);
// 第一个参数定义一个数 第二个参数定义方法 表示 额外一个数字10 参与stream流的取最大值
Integer add = stream2.reduce(10 , Math::max);
// 10
System.out.println(add);
三个参数:
Stream<Integer> stream1 = Stream.of(1, 5);
// 表示额外定义的10 参数累乘,并把结果(50)也参与 max取值
Integer add = stream1.reduce(10, (a, b) -> a * b, Math::max);
// 50
System.out.println(add);
二十四、stream流匹配写法 allMatch、anyMatch 和 noneMatch
allMatch 方法用于判断流中的所有元素是否都满足给定的条件。当流中的所有元素都满足条件时,返回 true;如果存在一个元素不满足条件,则返回 false。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allEven = numbers.stream()
.allMatch(n -> n % 2 == 0);
System.out.println(allEven); // 输出结果: false
anyMatch 方法用于判断流中是否存在至少一个元素满足给定的条件。当流中至少有一个元素满足条件时,返回 true;如果没有元素满足条件,则返回 false。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean hasEven = numbers.stream()
.anyMatch(n -> n % 2 == 0);
System.out.println(hasEven); //
noneMatch: noneMatch 方法用于判断流中的所有元素是否都不满足给定的条件。当流中没有元素满足条件时,返回 true;如果存在一个元素满足条件,则返回 false。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean noneNegative = numbers.stream()
.noneMatch(n -> n < 0);
System.out.println(noneNegative); // 输出结果: true
值得注意的是,我们匹配操作 不一定就要返回boolean, stream流操作都是可以结合使用的,如第18点提到的案例, 其实类似 if(true)的链式执行
List<String> resTemp = imports.stream()
.map(ApplyChannelExcelImportDTO::getSaleCompanyName)
.filter(name -> companyList.stream().filter(each -> each != null && StringUtils.isNotBlank(each.getName()))
.noneMatch(each -> each.getName().equals(name)))
.collect(Collectors.toList());
optional部分
Optional.of(value)
返回一个optional对象,option里面存放我们的value,通过option.get()获取,如果值为空,则报空指针,一般来说 我们既然使用了optional 那基本上都是希望避免空指针的,所以一般不用of
People p= null;
// 报 npe
Optional<People > optional = Optional.of(p);
Optional.ofNullable(value)
与 of(value)相似,ofNullable()里面可以存放空值,但是我们optional.get()出来的还是一个null, 大概率后续还是会出现空指针 ,什么意思呢? 我们举个例子:
People p= null;
// 不报 npe
Optional<People > optional = Optional.ofNullable(p);
// null
People p1 = optional.get();
// 报npe
p1.setName("用户名");
Optional.ofNullable(value).orElse(new Object())
所以我们一般会结合orElse使用,如果为空 则创建一个新对象
People p= null;
// 不报 npe
People p1 = Optional.ofNullable(p).orElse(new People ());
// 不报npe
p1.setName("用户名");
Optional.ofNullable(value).ifPresent(lambdaMethod)
ifPresent 里面是一个lambda表达式
People p= new People;
Optional.ofNullable(p).ifPresent(item ->{
item.setName("用户名");
});
// p.getName() --> 用户名 , 如果p为null 则跳过 也不报错 相当于无事发生
Optional.ofNullable(value).map(lambdaMethod).orElse(new Object())
这个应用场景类似于上面,多了一步orElse 意思是当 p 为null时,需要新创建一个对象,注:map是有返回值的
People p= new People;
People p1 = Optional.ofNullable(p).map(item ->{
item.setName("用户名");
return item;
}).orElse(new People());
// p.getName() --> 用户名 , 如果p为null 则跳过 也不报错 相当于无事发生