让数据流转换代码更加健壮流畅:List的Stream包装
为什么业务代码总是显得不够清晰 ? 因为开发人员总喜欢把业务与技术细节掺杂在一起。如下所示:
要改善这样的代码,需要将技术细节提取成可复用的基础库,而业务则可写成声明式的:
先创建一个 StreamUtil 工具类:
public class StreamUtil {
public static <T,R> List<R> map(List<T> data, Function<T, R> mapFunc) {
if (CollectionUtil.isEmpty(data)) { return new ArrayList(); }
return data.stream().map(mapFunc).collect(Collectors.toList());
}
public static <T> List<T> filter(List<T> data, Predicate<T> filterFunc) {
if (CollectionUtil.isEmpty(data)) { return new ArrayList(); }
return data.stream().filter(filterFunc).collect(Collectors.toList());
}
public static <T,R> List<R> filterAndMap(List<T> data, Predicate<T> filterFunc, Function<T, R> mapFunc) {
if (CollectionUtil.isEmpty(data)) { return new ArrayList(); }
return data.stream().filter(filterFunc).map(mapFunc).collect(Collectors.toList());
}
}
则原来 stream 流转换代码可写成:
rules = StreamUtil.filter(rules, rule -> BaselineUtils.matchPlatform(rule, platforms));
baseLineRules = StreamUtil.filter(rules, rule -> rule.getFamily() == BaselineRule.FAMILY_SYSTEM);
实际上, StreamUtil.filter 不如 rules.filter(testFunc) 更可读和流畅。如果能写成如下更好:
stream(rules).filter(rule -> BaselineUtils.matchPlatform(rule, platforms));
这需要对 List 做一个 Stream 的包装:
ListStream.java
/**
* @Description 列表的 Stream 包装
* @Date 2021/5/16 7:51 上午
* @Created by qinshu
*/
public class ListStream<T> {
private List<T> origin;
public ListStream(List<T> list) {
if (list == null) {
this.origin = new ArrayList<>();
}
else {
this.origin = list;
}
}
public static <T> ListStream<T> stream(List<T> list) {
return new ListStream<>(list);
}
public <R> List<R> map(Function<? super T, R> func) {
return origin.stream().map(func).collect(Collectors.toList());
}
public <R> Set<R> mapToSet(Function<? super T, R> func) {
return origin.stream().map(func).collect(Collectors.toSet());
}
public List<T> filter(Predicate<? super T> predicate) {
return origin.stream().filter(predicate).collect(Collectors.toList());
}
public <R> List<R> filterAndMapChain(List<Predicate<? super T>> beforeFilters,
Function<? super T,R> mapFunc, Predicate<R>... afterFilters) {
Stream<T> stream = origin.stream();
Stream<R> midResult = null;
if (beforeFilters != null) {
for (Predicate f: beforeFilters) {
stream = stream.filter(f);
}
}
if (mapFunc != null) {
midResult = stream.map(mapFunc);
}
if (afterFilters != null) {
for (Predicate<R> f: afterFilters) {
midResult = midResult.filter(f);
}
}
return midResult.collect(Collectors.toList());
}
}
单测:
public class ListStreamTest {
@Test
public void testNull() {
List<Integer> ints = stream(null).map(x -> (int)x + 1);
Assert.assertEquals(ints.size(), 0);
}
@Test
public void testMap() {
List<Integer> ints = stream(Arrays.asList(1,2,3,4,5)).map(x -> x*2);
Assert.assertArrayEquals(ints.toArray(new Integer[0]), new Integer[]{2,4,6,8,10});
}
@Test
public void testFilter() {
List<Integer> ints = stream(Arrays.asList(1,2,3,4,5)).filter(x -> x%2 ==0);
Assert.assertArrayEquals(ints.toArray(new Integer[0]), new Integer[]{2,4});
}
@Test
public void testFilterAndMapChain() {
List<Integer> ints = stream(Arrays.asList(1,2,3,4,5)).filterAndMapChain(
Arrays.asList(x -> x%2==1),
x -> x*2,
x -> x > 8);
Assert.assertArrayEquals(ints.toArray(new Integer[0]), new Integer[]{10});
}
}
这样,数据流转换代码就更加健壮和流畅。
很多人或许会觉得:把代码写得那么好图啥?其实,我是把代码表达当成一种思维的训练。 好代码不仅仅关乎习惯和态度,也关乎对设计和技术的理解。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了