1 lambda描述
Lambda 表达式是一种匿名函数,简单地说,它是没有声明的方法,也即没有访问修饰符、返回值声明和名字。
它可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使 Java 语言的表达能力得到了提升。
实际上,在开发过程中,也许会写出如下的代码
public boolean verifyRequestParam(JSONObject requestParam) {
JSONArray array = new JSONArray();
// 创建人脸识别任务,需要页面传入dataType, yqId, device, deviceName,deviceLocation,taskType, taskName
array.add(Constant.DATA_TYPE);
array.add(TaskEntity.TASK_TYPE);
array.add(TaskEntity.TASK_NAME);
array.add(Constant.YQ_ID);
array.add(Constant.DEVICE);
array.add(TaskEntity.DEVICE_NAME);
if (requestParam.containsKey(Constant.THRES)) {
try {
float thres = requestParam.getFloatValue(Constant.THRES);
if (thres < 0 || thres >= 1) {
log.error("请传入范围[0 - 1)的置信度");
return false;
}
} catch (JSONException | NumberFormatException e) {
e.printStackTrace();
log.error("请传入有效的浮点数置信度值");
return false;
}
}
return GeneralUtil.hasAllParams(requestParam, array);
}
从上面的语句中可以发现,程序首先构造了一个JSONArray,然后一个一个的添加,这很无聊,也会使得代码很无趣,那么有没有一种更具表达力,更凝练的代码表示形式,即实现相同的以图,又能使得代码的表达更加简洁有力呢?
在Java中lambda表达式就能为此提供很好的能力。
Lambda主要内容如下:
1.1 表现形式
1.2 方法分类
1.3 语法精简
1.4 方法引用
1.5 案例实践
1.6 系统内置函数式接口
具体函数定义参见下图:
Lambda表达式之函数式接口
1.7 闭包
## 1.8 函数式编程采用的观点和原则
2 Martin管道取代循环
在《重构 改善既有代码的设计中》,关于循环,Martin Fowler提出了两个重构手法,给我极大的震撼和指导意义。
2.1 拆分循环
因为在开发中,笔者也经常会编写一些身兼多职的循环,它们会一次做了两三件事情,不为别的,只因为这样可以只循环一次。但是,Fowoler认为如果在一次循环中做了两件事时,当需要修改循环时,你得同时理解两个循环。如果能够将循环拆分,让一个循环一次只做一件事,那么就能确保每次修改时你只需要理解要修改的那块代码的行为就可以了。
这次重构手法可能让许多程序员感到不安,因为它会迫使你执行两次循环。对此,Fowler建议:先进行重构,然后再进行性能优化,我得先让代码结构变得清晰,才能进一步优化;如果重构之后该循环确实成了性能的瓶颈,届时再把拆开的循环合到一起也很容易。但实际的情况是,即使处理的列表数据更多一些,循环本身也很少成为性能瓶颈,更何况拆分出循环来通常还使得一些更加强大的。
这个手法的意义不仅在于拆分出循环本身,而且在于它为进一步优化提供了良好的起点----下一步我通常会将每个循环提炼到独立的函数中。
2.2 管道代替循环
管道代替循环就是从重构2中获得的收获了,之前就一直对管道有好感,而且觉得stream和lambda很神秘,觉得无从下手。所以在智慧营区中还是决定小步前进,不断的使用小的lambda和stream,以小见大,慢慢对管道进行实践。
原来,在进行迭代一组集合时,使用循环。但随着时代的发展,如今越来越多的编程语言提供了更好的语言结构来处理迭代过程,这种结构叫做集合管道(collection pipeline),最常见的为map和filter莫属。map运算是指一个函数用作输入集合的每一个元素上,将集合变换成另一个集合的过程。filter运算是指一个函数从输入集合中筛选出符合条件的元素子集的过程。运算得到的集合可以供后续的流程使用。Martin Fowler发现,一些逻辑如果采用集合管道来编写,代码的可读性可以更强。只需要从头到尾阅读一遍代码,就能弄清对象在管道中间的变换过程。具体这个过程可以参考文章利用集合管道对循环进行函数式重构认真的理解。
2 lambda实践
2.1 样板代码
下属的代码很赞,因为通过实例来理解stream的使用,也是非常精彩的方式。而且非常可以方便学以致用。
package com.pancm.jdk8;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* @Title: StreamTest
* @Description: Stream测试用例 流的操作类型分为两种:
*
* Intermediate:一个流可以后面跟随零个或多个 intermediate
* 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。
* 这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。 Terminal:一个流只能有一个
* terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。 所以这必定是流的最后一个操作。 Terminal
* 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
* @Version:1.0.0
* @author pancm
* @date 2018年9月3日
*/
public class StreamTest {
/**
* @param args
*/
public static void main(String[] args) {
test1();
test2();
test3();
test4();
/*
* Stream 的特性可以归纳为:
不是数据结构
它没有内部存储,它只是用操作管道从 source(数据结构、数组、generator function、IO channel)抓取数据。
它也绝不修改自己所封装的底层数据结构的数据。例如 Stream 的 filter 操作会产生一个不包含被过滤元素的新 Stream,而不是从 source 删除那些元素。
所有 Stream 的操作必须以 lambda 表达式为参数
不支持索引访问
你可以请求第一个元素,但无法请求第二个,第三个,或最后一个。不过请参阅下一项。
很容易生成数组或者 List
惰性化
很多 Stream 操作是向后延迟的,一直到它弄清楚了最后需要多少数据才会开始。
Intermediate 操作永远是惰性化的。
并行能力
当一个 Stream 是并行化的,就不需要再写多线程代码,所有对它的操作会自动并行进行的。
可以是无限的
集合有固定大小,Stream 则不必。limit(n) 和 findFirst() 这类的 short-circuiting 操作可以对无限的 Stream 进行运算并很快完成。
*/
}
/**
* 简单实用
*/
private static void test1() {
/*
* 普通的方式过滤
*/
List<String> list = Arrays.asList("张三", "李四", "王五", "xuwujing");
System.out.println("过滤之前:" + list);
List<String> result = new ArrayList<>();
for (String str : list) {
if (!"李四".equals(str)) {
result.add(str);
}
}
System.out.println("过滤之后:" + result);
/*
* stream 过滤
*/
List<String> result2 = list.stream().filter(str -> !"李四".equals(str)).collect(Collectors.toList());
System.out.println("stream 过滤之后:" + result2);
// 另一种方式输出
result2.forEach(System.out::println);
// 使用stream.filter ()过滤一列表,并.findAny().orElse
// 遍历该list,查询数据,如果查不到,就返回 找不到!
String result3 = list.stream().filter(str -> "李四".equals(str)).findAny().orElse("找不到!");
String result4 = list.stream().filter(str -> "李二".equals(str)).findAny().orElse("找不到!");
System.out.println("stream 过滤之后 2:" + result3);
System.out.println("stream 过滤之后 3:" + result4);
//stream 过滤之后 2:李四
//stream 过滤之后 3:找不到!
}
/**
* 基本使用
*/
@SuppressWarnings({ "unchecked", "rawtypes", "unused" })
private static void test2() {
/*
* 构造流的几种方式
*/
Stream stream = Stream.of("a", "b", "c");
String[] strArray = new String[] { "a", "b", "c" };
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
List<String> list = Arrays.asList(strArray);
stream = list.stream();
/*
* 流之间的相互转化 一个 Stream 只可以使用一次,这段代码为了简洁而重复使用了数次,因此会抛出异常
*/
try {
Stream<String> stream2 = Stream.of("a", "b", "c");
// 转换成 Array
String[] strArray1 = stream2.toArray(String[]::new);
// 转换成 Collection
List<String> list1 = stream2.collect(Collectors.toList());
List<String> list2 = stream2.collect(Collectors.toCollection(ArrayList::new));
Set set1 = stream2.collect(Collectors.toSet());
Stack stack1 = stream2.collect(Collectors.toCollection(Stack::new));
// 转换成 String
String str = stream.collect(Collectors.joining()).toString();
} catch (Exception e) {
e.printStackTrace();
}
/*
* 汇总操作
*/
List<User> lists = new ArrayList<User>();
lists.add(new User(6, "张三"));
lists.add(new User(2, "李四"));
lists.add(new User(3, "王五"));
lists.add(new User(1, "张三"));
// 计算这个list中出现 "张三" id的值
int sum = lists.stream().filter(u -> "张三".equals(u.getName())).mapToInt(u -> u.getId()).sum();
System.out.println("计算结果:" + sum);
// 7
/*
* 数值类型的流 包括IntStream, LongStream和DoubleStream
*/
System.out.println("遍历输出该数组的数据:");
IntStream.of(new int[] { 1, 2, 3, 4 }).forEach(System.out::println);
System.out.println("查询范围在 2-3(2<=i<3)之间的数据:");
IntStream.range(2, 3).forEach(System.out::println);
System.out.println("查询范围在2-3(2<=i<=3)之间的数据:");
IntStream.rangeClosed(2, 3).forEach(System.out::println);
/* stream中的 map使用 */
/*
* 转换大写
*/
List<String> list3 = Arrays.asList("zhangSan", "liSi", "wangWu");
System.out.println("转换之前的数据:" + list3);
List<String> list4 = list3.stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println("转换之后的数据:" + list4);
// 转换之后的数据:[ZHANGSAN, LISI,WANGWU]
/*
* 转换数据类型
*/
List<String> list31 = Arrays.asList("1", "2", "3");
System.out.println("转换之前的数据:" + list31);
List<Integer> list41 = list31.stream().map(Integer::valueOf).collect(Collectors.toList());
System.out.println("转换之后的数据:" + list41);
// [1, 2, 3]
/*
* 转换数据类型
* 对象转map
*/
List<User> list32 = new ArrayList<User>();
for(int i=1;i<=10;i++){
list32.add(new User(i,"张三"+i));
}
System.out.println("转换之前的数据:" + list32);// 转换之前的数据:[1, 2, 3]
List<Map> list42 = list32.stream().map(User::toMap).collect(Collectors.toList());
System.out.println("转换之后的数据:" + list42); // [1, 2, 3]
/*
* 获取平方
*/
List<Integer> list5 = Arrays.asList(new Integer[] { 1, 2, 3, 4, 5 });
List<Integer> list6 = list5.stream().map(n -> n * n).collect(Collectors.toList());
System.out.println("平方的数据:" + list6);
// [1, 4, 9, 16, 25]
/*
* flatMap 一对多 得到多个数组里面的数字
*/
Stream<List<Integer>> inputStream = Stream.of(Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6));
Stream<Integer> outputStream = inputStream.flatMap((childList) -> childList.stream());
System.out.println("打印 stream中的数字:");
outputStream.forEach(System.out::println);
/*
* 得到一段句子中的单词
*/
String worlds = "The way of the future";
List<String> list7 = new ArrayList<>();
list7.add(worlds);
List<String> list8 = list7.stream().flatMap(str -> Stream.of(str.split(" ")))
.filter(world -> world.length() > 0).collect(Collectors.toList());
System.out.println("单词:");
list8.forEach(System.out::println);
// 单词:
// The
// way
// of
// the
// future
/*
* peek 对每个元素执行操作并返回一个新的 Stream
*/
System.out.println("peek使用:");
Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3).peek(e -> System.out.println("转换之前: " + e))
.map(String::toUpperCase).peek(e -> System.out.println("转换之后: " + e)).collect(Collectors.toList());
// 转换之前: three
// 转换之后: THREE
// 转换之前: four
// 转换之后: FOUR
/*
* limit 和 skip limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素(它是由一个叫
* subStream 的方法改名而来)。
*/
//limit 简单使用
Random rd = new Random();
System.out.println("取到的前三条数据:");
rd.ints().limit(3).forEach(System.out::println);
// 取到的前三条数据:
// 1167267754
// -1164558977
// 1977868798
List<User> list9 = new ArrayList<User>();
for (int i = 1; i < 4; i++) {
User user = new User(i, "pancm" + i);
list9.add(user);
}
System.out.println("截取之前的数据:");
// 取前3条数据,但是扔掉了前面的2条,可以理解为拿到的数据为 2<=i<3 (i 是数值下标)
List<String> list10 = list9.stream().map(User::getName).limit(3).skip(2).collect(Collectors.toList());
System.out.println("截取之后的数据:" + list10);
// 截取之前的数据:
// 姓名:pancm1
// 姓名:pancm2
// 姓名:pancm3
// 截取之后的数据:[pancm3]
/*
* sort 进行排序 先获取在排序效率更高
*/
Random rd2 = new Random();
System.out.println("取到的前三条数据然后进行排序:");
rd2.ints().limit(3).sorted().forEach(System.out::println);
// 取到的前三条数据然后进行排序:
// -2043456377
// -1778595703
// 1013369565
//普通的排序取值
List<User> list11 = list9.stream().sorted((u1, u2) -> u1.getName().compareTo(u2.getName())).limit(3)
.collect(Collectors.toList());
System.out.println("排序之后的数据:" + list11);
//优化排序取值
List<User> list12 = list9.stream().limit(3).sorted((u1, u2) -> u1.getName().compareTo(u2.getName()))
.collect(Collectors.toList());
System.out.println("优化排序之后的数据:" + list12);
//排序之后的数据:[{"id":1,"name":"pancm1"}, {"id":2,"name":"pancm2"}, {"id":3,"name":"pancm3"}]
//优化排序之后的数据:[{"id":1,"name":"pancm1"}, {"id":2,"name":"pancm2"}, {"id":3,"name":"pancm3"}]
/*
* min/max/distinct
* 最大,最小和去重
*/
List<String> list13 = Arrays.asList("zhangsan","lisi","wangwu","xuwujing");
int maxLines = list13.stream().mapToInt(String::length).max().getAsInt();
int minLines = list13.stream().mapToInt(String::length).min().getAsInt();
System.out.println("最长字符的长度:" + maxLines+",最短字符的长度:"+minLines);
//最长字符的长度:8,最短字符的长度:4
String lines = "good good study day day up";
List<String> list14 = new ArrayList<String>();
list14.add(lines);
List<String> words = list14.stream().flatMap(line -> Stream.of(line.split(" "))).filter(word -> word.length() > 0)
.map(String::toLowerCase).distinct().sorted().collect(Collectors.toList());
System.out.println("去重复之后:" + words);
//去重复之后:[day, good, study, up]
/*
* Match 匹配
*
* allMatch:Stream 中全部元素符合则返回 true ;
* anyMatch:Stream 中只要有一个元素符合则返回 true;
* noneMatch:Stream 中没有一个元素符合则返回 true。
*/
boolean all = lists.stream().allMatch(u -> u.getId() > 3);
System.out.println("是否都大于3:" + all);
boolean any = lists.stream().anyMatch(u -> u.getId() > 3);
System.out.println("是否有一个大于3:" + any);
boolean none = lists.stream().noneMatch(u -> u.getId() > 3);
System.out.println("是否没有一个大于3的:" + none);
// 是否都大于3:false
// 是否有一个大于3:true
// 是否没有一个大于3的:false
/*
* 生成随机数 通过实现 Supplier 接口,你可以自己来控制流的生成。这种情形通常用于随机数、常量的
* Stream,或者需要前后元素间维持着某种状态信息的 Stream。 把 Supplier 实例传递给 Stream.generate()
* 生成的 Stream,默认是串行(相对 parallel 而言)但无序的(相对 ordered 而言)。
* 由于它是无限的,在管道中,必须利用 limit 之类的操作限制 Stream 大小。
*/
Random seed = new Random();
seed.ints().limit(3).forEach(System.out::println);
Supplier<Integer> random = seed::nextInt;
System.out.println("生成5个随机数:");
Stream.generate(random).limit(3).forEach(System.out::println);
System.out.println("生成5正整数的随机数:");
IntStream.generate(() -> (int) (System.nanoTime() % 100)).limit(3).forEach(System.out::println);
System.out.println("生成5个随机数:");
/*
并行(parallel)程序
parallelStream 是流并行处理程序的代替方法。
*/
List<String> strings = Arrays.asList("a", "", "c", "", "e","", " ");
// 获取空字符串的数量
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("空字符串的个数:"+count);
/*
* 合并字符串
*/
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
// 筛选列表: [a, c, e, ]
// 合并字符串: a, c, e,
}
/**
* 一些关联使用
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private static void test3() {
/*
* Optional
*/
String strA = " abcd ", strB = null;
System.out.println("数据校验开始...");
print(strA);
print("");
print(strB);
getLength(strA);
getLength("");
getLength(strB);
System.out.println("数据校验结束...");
/*
* reduce 主要作用是把 Stream 元素组合起来。
*/
// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println("字符串拼接:" + concat);
//字符串拼接:ABCD
// 求最小值
double minValue = Stream.of(-4.0, 1.0, 3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println("最小值:" + minValue);
//最小值:-4.0
// 求和, 无起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println("有无起始值求和:" + sumValue);
// 求和, 有起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(1, Integer::sum);
System.out.println("有起始值求和:" + sumValue);
// 有无起始值求和:10
// 有起始值求和:11
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);
System.out.println("过滤和字符串连接:" + concat);
//过滤和字符串连接:ace
/*
* iterate iterate 跟 reduce 操作很像,接受一个种子值,和一个 UnaryOperator(例如 f)。
* 然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。 在 iterate
* 时候管道必须有 limit 这样的操作来限制 Stream 大小。
*/
System.out.println("从2开始生成一个等差队列:");
Stream.iterate(2, n -> n + 2).limit(5).forEach(x -> System.out.print(x + " "));
// 从2开始生成一个等差队列:
// 2 4 6 8 10
System.out.println("\n");
/*
* 分组排序 groupingBy/partitioningBy
*/
// 通过id进行排序
System.out.println("通过id进行分组排序:");
Map<Integer, List<User>> personGroups = Stream.generate(new UserSupplier2()).limit(5)
.collect(Collectors.groupingBy(User::getId));
Iterator it = personGroups.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Integer, List<User>> persons = (Map.Entry) it.next();
System.out.println("id " + persons.getKey() + " = " + persons.getValue());
}
// 通过id进行分组排序:
// id 10 = [{"id":10,"name":"pancm1"}]
// id 11 = [{"id":11,"name":"pancm3"}, {"id":11,"name":"pancm6"}, {"id":11,"name":"pancm4"}, {"id":11,"name":"pancm7"}]
//通过年龄排序
System.out.println("通过年龄进行分区排序:");
Map<Boolean, List<User>> children = Stream.generate(new UserSupplier3()).limit(5)
.collect(Collectors.partitioningBy(p -> p.getId() < 18));
System.out.println("小孩: " + children.get(true));
System.out.println("成年人: " + children.get(false));
// 通过年龄进行分区排序:
// 小孩: [{"id":16,"name":"pancm7"}, {"id":17,"name":"pancm2"}]
// 成年人: [{"id":18,"name":"pancm4"}, {"id":19,"name":"pancm9"}, {"id":20,"name":"pancm6"}]
/*
* IntSummaryStatistics 用于收集统计信息(如count、min、max、sum和average)的状态对象。
*/
List<Integer> numbers = Arrays.asList(1, 5, 7, 3, 9);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
// 列表中最大的数 : 9
// 列表中最小的数 : 1
// 所有数之和 : 25
// 平均数 : 5.0
}
/**
* 自定义流
*/
private static void test4() {
/*
* 自定义一个流 然后进行输出
*/
System.out.println("自定义一个流进行计算输出:");
Stream.generate(new UserSupplier()).limit(2).forEach(u -> System.out.println(u.getId() + ", " + u.getName()));
//第一次:
//自定义一个流进行计算输出:
//10, pancm7
//11, pancm6
//第二次:
//自定义一个流进行计算输出:
//10, pancm4
//11, pancm2
//第三次:
//自定义一个流进行计算输出:
//10, pancm4
//11, pancm8
}
public static void print(String text) {
// jdk1.8之前的写法
// if (text != null) {
// System.out.println(text);
// }
// jdk1.8的写法
Optional.ofNullable(text).ifPresent(System.out::println);
}
public static void getLength(String text) {
// jdk1.8之前的写法
// return if (text != null) ? text.length() : -1;
// jdk1.8的写法
int i = Optional.ofNullable(text).map(String::length).orElse(-1);
System.out.println("数据:" + i);
};
}
class UserSupplier implements Supplier<User> {
private int index = 10;
private Random random = new Random();
@Override
public User get() {
return new User(index++, "pancm" + random.nextInt(10));
}
}
class UserSupplier2 implements Supplier<User> {
private int index = 10;
private Random random = new Random();
@Override
public User get() {
return new User(index % 2 == 0 ? index++ : index, "pancm" + random.nextInt(10));
}
}
class UserSupplier3 implements Supplier<User> {
private int index = 16;
private Random random = new Random();
@Override
public User get() {
return new User(index++, "pancm" + random.nextInt(10));
}
}
具体内容可以参考github项目java学习项目
2.2 智慧营区项目中实践
2.2.1 主机端口获取
通过Arrays.asList()把多个同类型的成员组织成List,进而获取stream
public static String getHostPort(String ip, String port) {
List<String> urlElements = Arrays.asList(HTTP_PROCTOL, ip, ":", port);
return urlElements.stream().collect(Collectors.joining(""));
}
这主要是因为在ServerConfig类中出现了很多的重复模式
"http" + ip +":" +port
因此可以使用getHostPort函数进行重构,如下所示:
package com.cetc52.camp.config;
import com.cetc52.camp.util.GeneralUtil;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 获取项目的IP和端口
*
* @author songquanheng
* @Time: 2019/2/22-16:21
*/
@Component
@Data
public class ServerConfig {
@Value("${retrieval.ip}")
private String retrievalIp;
@Value("${retrieval.port}")
private String retrievalPort;
@Value("${camp.ip}")
private String serverIp;
@Value("${camp.port}")
private String serverPort;
@Value("${inferEngine.ip}")
private String inferEngineIp;
@Value("${inferEngine.port}")
private String inferEnginePort;
public String getRetrievalUrl() {
return GeneralUtil.getHostPort(retrievalIp, retrievalPort);
}
public String getServerUrl() {
return GeneralUtil.getHostPort(serverIp, serverPort);
}
public String getInferEngineUrl() {
return GeneralUtil.getHostPort(inferEngineIp, inferEnginePort);
}
}
重复代码(Duplicate Code),如果你在一个以上的地点看到相同的代码结构,那么可以肯定:设法将它们合二为一,程序会变得更好。一旦有重复的代码存在,阅读这些重复的代码时,你就必须加倍仔细,留意其间细微的差异。如果要修改重复的代码时,你就必须找出所有的副本来修改(重构第二版p72)。在Clean Code 一书中,Robert也是如此编写的。程序应该遵守DRY原则,重复的是罪恶的源泉,遵守DRY: Donot Repeat Yourself原则,发现代码中的重复之处,这样你会渐渐发现程序的质量逐渐就会有质的提升。
下述代码为使用代码重构之前的形式:
package com.cetc52.camp.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* 获取项目的IP和端口
*
* @author songquanheng
* @Time: 2019/2/22-16:21
*/
@Component
@Data
public class ServerConfig {
public static final String HTTP_PROCTOL = "http://";
@Value("${retrieval.ip}")
private String retrievalIp;
@Value("${retrieval.port}")
private String retrievalPort;
@Value("${camp.ip}")
private String serverIp;
@Value("${camp.port}")
private String serverPort;
@Value("${inferEngine.ip}")
private String inferEngineIp;
@Value("${inferEngine.port}")
private String inferEnginePort;
public String getRetrievalUrl() {
return HTTP_PROCTOL + retrievalIp + ":" + retrievalPort;
}
public String getServerUrl() {
return HTTP_PROCTOL + serverIp + ":" + serverPort;
}
public String getInferEngineUrl() {
return HTTP_PROCTOL + inferEngineIp + ":" + inferEnginePort;
}
}
可以看到通过更积极的分解函数,代码变得更加凝练,而且出错的概率更小,因为写出的函数已经得到了很好的测试,在撰写代码时,你会更加自信,代码也会变得更加优雅。
2.2.2 定时任务修改map的实践,并且移除map中key-value
private static Map<String, AreaInvade> areaInvadeMap = new HashMap<>();
private static Map<String, AreaRetention> areaRetentionMap = new HashMap<>();
private static Map<String, CrossBorder> crossBorderMap = new HashMap<>();
private static ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1,
new BasicThreadFactory.Builder().namingPattern("object-detection-pool-%d").daemon(true).build());
/**
* 区域入侵分析器
*/
private AreaInvade areaInvade;
/**
* 徘徊侦测分析器
*/
private AreaRetention areaRetention;
/**
* 跨越边界分析器
*/
private CrossBorder crossBorder;
static {
scheduledExecutorService.scheduleAtFixedRate(() -> {
cancelDeletedTaskAnalyzer();
removeDeletedTaskAnalyzer();
cancelNotRunningTaskAnalyzer();
removeNotRunningTaskAnalyzer();
}, 10, 5, TimeUnit.SECONDS);
}
/**
* 应对安防任务终止即删除的情形。取消已经删除的安防任务分析器中定时任务以及保存的人车物id数据集合。
*/
public static void cancelDeletedTaskAnalyzer() {
areaInvadeMap.forEach((actualTaskId, areaInvade) -> {
if (taskHasBeenDeleted(actualTaskId)) {
log.info("取消一个已经被删除的安防任务的区域入侵分析器");
areaInvade.getTimer().cancel();
areaInvade.clear();
}
});
areaRetentionMap.forEach((actualTaskId, areaRetention) -> {
if (taskHasBeenDeleted(actualTaskId)) {
log.info("取消一个已经被删除的安防任务的区域徘徊侦测分析器");
areaRetention.getTimer().cancel();
areaRetention.clear();
}
});
crossBorderMap.forEach((actualTaskId, crossBorder) -> {
if (taskHasBeenDeleted(actualTaskId)) {
log.info("取消一个已经被删除的安防任务的越界侦测分析器");
crossBorder.getTimer().cancel();
crossBorder.clear();
}
});
}
/**
* 应对安防任务终止即终止立即删除任务的情形。取消已经删除的安防任务分析器。
* 此时定时任务5秒之后运行根据actual_task_id获取可能获取不到,返回null的问题。
*/
public static void removeDeletedTaskAnalyzer() {
areaInvadeMap.keySet().removeIf(actualTaskId -> taskHasBeenDeleted(actualTaskId));
areaRetentionMap.keySet().removeIf(actualTaskId -> taskHasBeenDeleted(actualTaskId));
areaRetentionMap.keySet().removeIf(actualTaskId -> taskHasBeenDeleted(actualTaskId));
}
private static boolean taskHasBeenDeleted(String actualTaskId) {
List<String> actualTaskIds = allTaskEntitiesFromDataBase();
return !actualTaskIds.contains(actualTaskId);
}
private static List<String> allTaskEntitiesFromDataBase() {
List<TaskEntity> taskEntities = taskMapper.selectAll();
return taskEntities.stream().map(taskEntity1 -> taskEntity1.getActualTaskId()).collect(Collectors.toList());
}
/**
* 取消一个安防分析器的定时任务并清空其中保存的人、车、物id
*/
private static void cancelNotRunningTaskAnalyzer() {
areaInvadeMap.forEach((actualTaskId, areaInvade) -> {
if (taskNotRunning(taskMapper.selectByTaskId(actualTaskId))) {
log.info("取消一个区域入侵分析器");
areaInvade.getTimer().cancel();
areaInvade.clear();
}
});
areaRetentionMap.forEach((actualTaskId, areaRetention) -> {
if (taskNotRunning(taskMapper.selectByTaskId(actualTaskId))) {
log.info("取消一个区域徘徊侦测分析器");
areaRetention.getTimer().cancel();
areaRetention.clear();
}
});
crossBorderMap.forEach((actualTaskId, crossBorder) -> {
if (taskNotRunning(taskMapper.selectByTaskId(actualTaskId))) {
log.info("取消一个越界侦测分析器");
crossBorder.getTimer().cancel();
crossBorder.clear();
}
});
}
/**
* 移除不再运行的安防分析器
*/
private static void removeNotRunningTaskAnalyzer() {
areaInvadeMap.keySet().removeIf(actualTaskId -> taskNotRunning(taskMapper.selectByTaskId(actualTaskId)));
areaRetentionMap.keySet().removeIf(actualTaskId -> taskNotRunning(taskMapper.selectByTaskId(actualTaskId)));
crossBorderMap.keySet().removeIf(actualTaskId -> taskNotRunning(taskMapper.selectByTaskId(actualTaskId)));
}
private static boolean taskNotRunning(TaskEntity taskEntity) {
return !TaskStatus.RUNNING.getValue().equalsIgnoreCase(taskEntity.getTaskStatus());
}
从最早的编程语言开始,循环就一直是程序设计的核心要素。但我已经开始渐渐鄙视循环语句,如今,函数已经作为一等公民得到了广泛的支持,因此我们可以对使用管道替换循环来让这些循环退休。并且保证循环语句也遵循单一工作。即做一件事,也只做一件事。
上述的代码中,前三条语句用于更新定时任务中的areaRetentionMap所持有的areaRetention分析器的定时任务和持有的物品映射集合。后三条语句用于使用lambda表达式移除不再处于运行状态的taskEntity的areaRentention。
关于map中删除key-value可以通过下面的代码片段理解:
// 通过value移除
map.values().removeIf(value -> !value.contains("1"));
// 通过key移除
map.keySet().removeIf(key -> key != 1);
// 通过键/值的输入/组合删除
map.entrySet().removeIf(entry -> entry.getKey() != 1);
具体理解可以参考Java删除Map中元素
2.2.3 字符串合并
/**
* 用于合并字符串
* @param elements 用于合并字符串,默认分隔符为空""
* @return 返回合并后的字符串
*/
public static String mergeString(String... elements) {
return Arrays.stream(elements).collect(Collectors.joining(StringUtils.EMPTY));
}
此处与getHostPort函数类似,不再赘述。不过使用了可变字符串参数。可变参数实践可以参见Arrays.asList()函数。
/**
* Returns a fixed-size list backed by the specified array. (Changes to
* the returned list "write through" to the array.) This method acts
* as bridge between array-based and collection-based APIs, in
* combination with {@link Collection#toArray}. The returned list is
* serializable and implements {@link RandomAccess}.
*
* <p>This method also provides a convenient way to create a fixed-size
* list initialized to contain several elements:
* <pre>
* List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
* </pre>
*
* @param <T> the class of the objects in the array
* @param a the array by which the list will be backed
* @return a list view of the specified array
*/
@SafeVarargs
@SuppressWarnings("varargs")
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
需要注意,可变长参数应该为函数的最后一个参数,而且若只有两个参数,则另一个参数不能与可变长参数的类型相同。在编写合并字符串时,笔者曾想要赋予mergeString函数一个delimiter的参数,用于指定分隔符。结果发现了这个问
题。
2.2.4 组合JSONObject
/**
* 待存储到检索平台的检测识别结果
*
* @param recognitionResult 一条检索结果
* @param bgAndTime 背景大图的信息与发生时间。
* @param deviceInfo 设备信息,主要为设备id,设备名称
* @return 返回存储到检索平台的检测识别结果
*/
protected JSONObject organizeRetrievalResult(JSONObject recognitionResult,
JSONObject bgAndTime, JSONObject deviceInfo) {
JSONObject result = JSONObject.parseObject(recognitionResult.toJSONString());
bgAndTime.forEach((k, v) -> {
result.put(k, v);
});
deviceInfo.forEach((k, v) -> {
result.put(k, v);
});
return result;
}
2.2.5 异步发送事件
/**
* @param eventReport 上报参数
* @param taskType 任务类型
* @description 接受上报数据
*/
@Async
public void reportIntelligentAnalysis(String taskType, JSONObject eventReport) {
log.info("Enter reportIntelligentAnalysis");
log.info("发生:" + taskType + "事件,上报数据" + eventReport.toJSONString());
sendEvent(eventReport);
List<String> urls = uploadUrlManagerService.selectByTaskType(taskType);
urls.stream().forEach(url -> {
send(url, eventReport);
});
log.info("Leave reportIntelligentAnalysis");
}
2.3 实例阐述
Stream作为java8的新特性,基于lambda表达式,是对集合对象功能的增强,它专注于对集合对象进行各种高效、便利的聚合操作或者大批量的数据操作,提高了编程效率和代码的可读性。
Stream的原理:将要处理的元素看作一种流,流在管道中传输,并且可以在管道的节点上进行处理,包括过滤筛选、去重、排序、聚合。元素流在管道中经过中间操作的处理,最终由最终操作得到前面处理的结果。
中间操作:
中间操作主要有以下方法(此类型方法返回的都是 Stream):map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
终止操作主要有以下方法:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
2.3.1 演示使用model
package com.example.profile.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @Owner: SongQuanHeng
* @Time: 2019/10/19-11:20
* @Version:
* @Change:
*/
public class Student {
private Long id;
private String name;
private int age;
private String address;
public Student() {
}
public Student(Long id, String name,int age,String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "Student{"+"id="+id+
", name="+name+", age="+age+", address="+address+"}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(id, student.id) &&
Objects.equals(name, student.name) &&
Objects.equals(address, student.address);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age, address);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public static void main(String[] args) {
Student s1 = new Student(1L, "肖战", 15, "浙江");
Student s2 = new Student(2L, "周云", 18, "四川");
Student s3 = new Student(3L, "宋信", 50, "河北");
Student s4 = new Student(4L, "王菲", 28, "浙江");
List<Student> students = new ArrayList<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s4);
System.out.println(students.stream().filter(s -> s.getAge() > 20).collect(Collectors.toList()));
List<Student> streamStudents = testFilter(students);
streamStudents.forEach(System.out::println);
}
/**
* 集合的筛选
* @param students
* @return
*/
private static List<Student> testFilter(List<Student> students) {
//筛选年龄大于15岁的学生
// return students.stream().filter(s -> s.getAge()>15).collect(Collectors.toList());
//筛选住在浙江省的学生
return students.stream().filter(s -> "浙江".equals(s.getAddress())).collect(Collectors.toList());
}
}
2.3.2 数据准备
public static void main(String[] args) {
Student s1 = new Student(1L, "肖战", 15, "浙江");
Student s2 = new Student(2L, "周云", 18, "四川");
Student s3 = new Student(3L, "宋信", 50, "河北");
Student s4 = new Student(4L, "王菲", 28, "浙江");
List<Student> students = new ArrayList<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s4);
System.out.println(students.stream().filter(s -> s.getAge() > 20).collect(Collectors.toList()));
List<Student> streamStudents = testFilter(students);
streamStudents.forEach(System.out::println);
}
/**
* 集合的筛选
* @param students
* @return
*/
private static List<Student> testFilter(List<Student> students) {
//筛选年龄大于15岁的学生
// return students.stream().filter(s -> s.getAge()>15).collect(Collectors.toList());
//筛选住在浙江省的学生
return students.stream().filter(s -> "浙江".equals(s.getAddress())).collect(Collectors.toList());
}
输出如下:
[Student{id=3, name=宋信, age=50, address=河北}, Student{id=4, name=王菲, age=28, address=浙江}]
Student{id=1, name=肖战, age=15, address=浙江}
Student{id=4, name=王菲, age=28, address=浙江}
上述代码演示了过滤的用途,最终操作是形成一个新的List。
2.3.3 map转换
2.3.3.1 项目实践
private static List<String> allTaskEntitiesFromDataBase() {
List<TaskEntity> taskEntities = taskMapper.selectAll();
return taskEntities.stream().map(taskEntity1 -> taskEntity1.getActualTaskId()).collect(Collectors.toList());
}
上述的代码逻辑是把List转换成了List
2.3.3.2 map实例演示
Student s1 = new Student(1L, "肖战", 15, "浙江");
Student s2 = new Student(2L, "周云", 18, "四川");
Student s3 = new Student(3L, "宋信", 50, "河北");
Student s4 = new Student(4L, "王菲", 28, "浙江");
List<Student> students = new ArrayList<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s4);
List<String> addresses = students.stream().map(student -> "住址: "+student.getAddress()).collect(Collectors.toList());
addresses.forEach(s->System.out.println(s));
演示结果如下:
住址: 浙江
住址: 四川
住址: 河北
住址: 浙江
2.3.4 去重
2.3.4.1 基础类型去重
List<String> list = Arrays.asList("111", "222", "333", "111", "222");
list.stream().distinct().forEach(System.out::println);
演示结果如下:
111
222
333
2.3.4.2 引用对象去重
Student s1 = new Student(1L, "肖战", 15, "浙江");
Student s2 = new Student(2L, "周云", 18, "四川");
Student s3 = new Student(2L, "周云", 18, "四川");
Student s5 = new Student(4L, "王菲", 28, "浙江");
Student s6 = new Student(4L, "王菲", 28, "浙江");
List<Student> students = new ArrayList<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s5);
students.add(s6);
students.stream().distinct().forEach(System.out::println);
演示结果如下:
Student{id=1, name=肖战, age=15, address=浙江}
Student{id=2, name=周云, age=18, address=四川}
Student{id=4, name=王菲, age=28, address=浙江}
注意:引用对象的去重要实现hashCode和equals,否则去重无效。
2.3.5 sorted排序
2.3.5.1 基础类型排序
List<String> list1 = Arrays.asList("333", "555", "222", "444", "111");
list1.stream().sorted().forEach(System.out::println);
演示结果:
111
222
333
444
555
2.3.5.2 引用类型排序
Student s1 = new Student(1L, "肖战", 15, "浙江");
Student s2 = new Student(2L, "周云", 18, "四川");
Student s3 = new Student(2L, "周云", 10, "四川");
Student s5 = new Student(4L, "王菲", 28, "浙江");
Student s6 = new Student(4L, "王菲", 28, "浙江");
List<Student> students = new ArrayList<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s5);
students.add(s6);
students.stream()
.sorted((student1, student2) -> Long.compare(student1.getId(), student2.getId()))
.forEach(System.out::println);
System.out.println("=====================");
students.stream()
.sorted((student1, student2) -> Long.compare(student1.getId(), student2.getId()))
.sorted((student1, student2)->Integer.compare(student1.getAge(), student2.getAge()))
.forEach(System.out::println);
演示结果如下:
Student{id=1, name=肖战, age=15, address=浙江}
Student{id=2, name=周云, age=18, address=四川}
Student{id=2, name=周云, age=10, address=四川}
Student{id=4, name=王菲, age=28, address=浙江}
Student{id=4, name=王菲, age=28, address=浙江}
=====================
Student{id=2, name=周云, age=10, address=四川}
Student{id=1, name=肖战, age=15, address=浙江}
Student{id=2, name=周云, age=18, address=四川}
Student{id=4, name=王菲, age=28, address=浙江}
Student{id=4, name=王菲, age=28, address=浙江}
2.3.6 limit(限制返回个数)
List<String> list1 = Arrays.asList("333", "555", "222", "444", "111");
list1.stream().limit(3).forEach(System.out::println);
System.out.println("limit=====================");
演示结果:
333
555
222
limit=====================
2.3.7 skip(删除元素)
List<String> list2 = Arrays.asList("111", "222", "333", "111", "222");
list2.stream().skip(2).forEach(System.out::println);
演示结果:
333
111
222
2.3.8 reduce(聚合)
List<String> list3 = Arrays.asList("欢", "迎", "你");
String appendStr = list3.stream().reduce("北京", (a, b) -> a + b);
System.out.println(appendStr);
演示结果
北京欢迎你
关于reduce函数,注解如下
/**
* Performs a <a href="package-summary.html#Reduction">reduction</a> on the
* elements of this stream, using the provided identity value and an
* <a href="package-summary.html#Associativity">associative</a>
* accumulation function, and returns the reduced value. This is equivalent
* to:
* <pre>{@code
* T result = identity;
* for (T element : this stream)
* result = accumulator.apply(result, element)
* return result;
* }</pre>
*
* but is not constrained to execute sequentially.
*
* <p>The {@code identity} value must be an identity for the accumulator
* function. This means that for all {@code t},
* {@code accumulator.apply(identity, t)} is equal to {@code t}.
* The {@code accumulator} function must be an
* <a href="package-summary.html#Associativity">associative</a> function.
*
* <p>This is a <a href="package-summary.html#StreamOps">terminal
* operation</a>.
*
* @apiNote Sum, min, max, average, and string concatenation are all special
* cases of reduction. Summing a stream of numbers can be expressed as:
*
* <pre>{@code
* Integer sum = integers.reduce(0, (a, b) -> a+b);
* }</pre>
*
* or:
*
* <pre>{@code
* Integer sum = integers.reduce(0, Integer::sum);
* }</pre>
*
* <p>While this may seem a more roundabout way to perform an aggregation
* compared to simply mutating a running total in a loop, reduction
* operations parallelize more gracefully, without needing additional
* synchronization and with greatly reduced risk of data races.
*
* @param identity the identity value for the accumulating function
* @param accumulator an <a href="package-summary.html#Associativity">associative</a>,
* <a href="package-summary.html#NonInterference">non-interfering</a>,
* <a href="package-summary.html#Statelessness">stateless</a>
* function for combining two values
* @return the result of the reduction
*/
T reduce(T identity, BinaryOperator<T> accumulator);
2.3.9 min(求最小值)
Student s1 = new Student(1L, "肖战", 15, "浙江");
Student s2 = new Student(2L, "周云", 18, "四川");
Student s3 = new Student(2L, "周云", 10, "四川");
Student s5 = new Student(4L, "王菲", 28, "浙江");
Student s6 = new Student(4L, "王菲", 28, "浙江");
List<Student> students = new ArrayList<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s5);
students.add(s6);
Student minS = students.stream().min((stu1, stu2) -> Integer.compare(stu1.getAge(), stu2.getAge())).get();
System.out.println(minS);
演示结果:
Student{id=2, name=周云, age=10, address=四川}
2.3.10 anyMatch/allMatch/noneMatch(匹配)
Student s1 = new Student(1L, "肖战", 15, "浙江");
Student s2 = new Student(2L, "周云", 18, "四川");
Student s3 = new Student(2L, "周云", 10, "四川");
Student s5 = new Student(4L, "王菲", 28, "浙江");
Student s6 = new Student(4L, "王菲", 28, "浙江");
List<Student> students = new ArrayList<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s5);
students.add(s6);
Boolean anyMatch = students.stream().anyMatch(student -> "浙江".equals(s1.address));
if (anyMatch) {
System.out.println("有浙江人");
}
Boolean allMatch = students.stream().anyMatch(student -> student.getAge()>=10);
if (allMatch) {
System.out.println("所有学生都满10周岁");
}
Boolean noneMatch = students.stream().noneMatch(student -> student.getName()=="阳阳");
if (noneMatch) {
System.out.println("没有叫阳阳的同学");
}
演示结果如下
有浙江人
所有学生都满10周岁
没有叫阳阳的同学
2.3.11 演示代码
package com.example.profile.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @Owner: SongQuanHeng
* @Time: 2019/10/19-11:20
* @Version:
* @Change:
*/
public class Student {
private Long id;
private String name;
private int age;
private String address;
public Student() {
}
public Student(Long id, String name,int age,String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
@Override
public String toString() {
return "Student{"+"id="+id+
", name="+name+", age="+age+", address="+address+"}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(id, student.id) &&
Objects.equals(name, student.name) &&
Objects.equals(address, student.address);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age, address);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public static void main(String[] args) {
Student s1 = new Student(1L, "肖战", 15, "浙江");
Student s2 = new Student(2L, "周云", 18, "四川");
Student s3 = new Student(2L, "周云", 10, "四川");
Student s5 = new Student(4L, "王菲", 28, "浙江");
Student s6 = new Student(4L, "王菲", 28, "浙江");
List<Student> students = new ArrayList<>();
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s5);
students.add(s6);
Boolean anyMatch = students.stream().anyMatch(student -> "浙江".equals(s1.address));
if (anyMatch) {
System.out.println("有浙江人");
}
Boolean allMatch = students.stream().anyMatch(student -> student.getAge()>=10);
if (allMatch) {
System.out.println("所有学生都满10周岁");
}
Boolean noneMatch = students.stream().noneMatch(student -> student.getName()=="阳阳");
if (noneMatch) {
System.out.println("没有叫阳阳的同学");
}
System.out.println("=====================");
Student minS = students.stream().min((stu1, stu2) -> Integer.compare(stu1.getAge(), stu2.getAge())).get();
System.out.println(minS);
students.stream()
.sorted((student1, student2) -> Long.compare(student1.getId(), student2.getId()))
.forEach(System.out::println);
System.out.println("=====================");
students.stream()
.sorted((student1, student2) -> Long.compare(student1.getId(), student2.getId()))
.sorted((student1, student2)->Integer.compare(student1.getAge(), student2.getAge()))
.forEach(System.out::println);
System.out.println("=====================");
List<String> list1 = Arrays.asList("333", "555", "222", "444", "111");
list1.stream().limit(3).forEach(System.out::println);
System.out.println("limit=====================");
list1.stream().sorted().forEach(System.out::println);
System.out.println("sorted=====================");
students.stream().distinct().forEach(System.out::println);
List<String> addresses = students.stream().map(student -> "住址: "+student.getAddress()).collect(Collectors.toList());
addresses.forEach(s->System.out.println(s));
List<String> list = Arrays.asList("111", "222", "333", "111", "222");
list.stream().distinct().forEach(System.out::println);
System.out.println("=====================");
List<String> list2 = Arrays.asList("111", "222", "333", "111", "222");
list2.stream().skip(2).forEach(System.out::println);
System.out.println("=====================");
List<String> list3 = Arrays.asList("欢", "迎", "你");
String appendStr = list3.stream().reduce("北京", (a, b) -> a + b);
System.out.println(appendStr);
// System.out.println(students.stream().filter(s -> s.getAge() > 20).collect(Collectors.toList()));
// List<Student> streamStudents = testFilter(students);
// streamStudents.forEach(System.out::println);
}
/**
* 集合的筛选
* @param students
* @return
*/
private static List<Student> testFilter(List<Student> students) {
//筛选年龄大于15岁的学生
// return students.stream().filter(s -> s.getAge()>15).collect(Collectors.toList());
//筛选住在浙江省的学生
return students.stream().filter(s -> "浙江".equals(s.getAddress())).collect(Collectors.toList());
}
}
3 总结
从开始对于lambda表达式与stream好感,但慢慢实践,再由重构第二版Martin Fowler把使用管道取代循环作为一个让我醍醐灌顶的重构手法来阐述,自己在智慧营区项目中也进行了相应的stream实践,也确实从中体会到了管道的优雅之处。不管怎样,自己会在以后的编程生活中抵制循环,拥抱管道,不断锤炼自己的代码。
2019年10月16日00:16:26于运粮河汉庭8350
2019-10-19 12:50于湖墅新村
2019-12-02 21:54于湖墅新村