最近又找了个lambda表达式的课程,来记录下笔记
一.lambda表达式
好处是不用写参数的类型
1.等号左边是接口,等号右边是实现
Itest itest = (s1, s2) -> {
System.out.println("sssss");
return "123";
};
System.out.println(itest.test("s1","s2"));
2.最经典的Runnable写法
//改lambda前
Thread test = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("test");
}
});
test.start();
//改lambda后
Thread testLambda = new Thread(() -> System.out.println("test"));
testLambda.start();
3.各种测试代码
System.out.println(itest.test("s1","s2"));*/
Comparator<Integer> comparator = Comparator.comparingInt(o -> o);
List<Integer> list = new ArrayList<>();
list.add(9);
list.add(3);
list.add(4);
list.stream().forEach(System.out::println);
System.out.println("------------------------------");
//filter过滤
list.stream().filter(integer -> integer>4).forEach(System.out::println);
lombok的@ToString注解
TestEntity(i=1, j=2)
TestEntity testEntity1 = new TestEntity(1,2);
TestEntity testEntity2 = new TestEntity(3,4);
//快速拼数组
List<TestEntity> testEntities = Arrays.asList(testEntity1, testEntity2);
testEntities.stream().forEach(System.err::println);
输出
TestEntity(i=1, j=2)
TestEntity(i=3, j=4)
4.插播一个@ToString注解
二.四大核心函数
四大函数 可以看到 他们都是泛型
1.消费型接口
消费型是需要入参的 但是没有返回 我给你什么 你把他给我消费 比如我们用foreach方法时就会用到消费型接口
Consumer<String> consumer = System.out::println;
2.供给型接口
供给型是没有入参的 但是有返回 返回什么拿什么 而不是你再上传给我
Supplier<Integer> supplier = () -> 100;
3.函数型接口
函数型接口很常用,有上传T 也有返回R 比如我们用map方法时就会用到函数式接口 flatmap时也会用函数式接口
Function<String, Integer> function = s -> Integer.valueOf(s) + 100;//就一行代码 不用写return 也不用大括号{}
System.out.println(function.apply("10"));
4.断言型接口
断言型接口 有上传 返回是Boolean 比如我们用filter方法时就会用到断言型接口
Predicate<Integer> predicate = integer -> {//多行代码 需要大括号{} return也不能省
if(integer==2){
return true;
}else{
return false;
}
};
三.创建stream的方式
1.对stream的操作有这样几步
1.1.创建流
1.2.操作流
1.3.终止流
2.创建流的几种方式
1.集合转换为流
最常用
list.stream()
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Stream<String> stream1 = list.stream();
stream1.forEach(System.out::println);
2.数组转换为流
Arrays.stream()
String[] strArr = new String[2];
strArr[0] = "3";
strArr[1] = "4";
Stream<String> stream2 = Arrays.stream(strArr);
stream2.forEach(System.out::println);
3.流的静态方法
Stream.of(1,2,3)
Stream<Integer> stream3 = Stream.of(5,6);
stream3.forEach(System.out::println);
4.创建一个没有限制的流
项目里还没看谁用过
Stream<Integer> stream4 = Stream.iterate(0, integer -> integer + 1);
stream4.forEach(System.out::println);//这里会一直打印 除非强制停止
四.flatMap
把几个小的list转换到一个大的list
可参考链接:java8 map flatmap - 穆穆兔兔 - 博客园
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
List<Integer> list2 = new ArrayList<>();
list2.add(3);
list2.add(4);
List<Integer> list3 = new ArrayList<>();
list3.add(5);
list3.add(6);
List<List<Integer>> bigList = new ArrayList<>();
bigList.add(list1);
bigList.add(list2);
bigList.add(list3);
//flatmap 大list转为小list
//以下三种处理方式是一个效果 都是打印出了123456
List<Integer> collect1 = bigList.stream().flatMap(Collection::stream).collect(Collectors.toList());
collect1.forEach(System.out::println);
System.out.println("----------------------------------------------------");
List<Integer> collect2 = bigList.stream().flatMap(everyList -> everyList.stream()).collect(Collectors.toList());
collect2.forEach(System.out::println);
System.out.println("----------------------------------------------------");
List<Integer> collect3 = bigList.stream().flatMap((Function<List<Integer>, Stream<Integer>>) integers -> integers.stream()).collect(Collectors.toList());
collect3.forEach(System.out::println);
五.多属性排序
5.1thenComparing
List<Person> people = Arrays.asList(
//字符串比较是按照字母顺序一个一个比较
new Person(3, "bbc"), new Person(3, "caa"),
new Person(5, "55"), new Person(6, "66"),
new Person(1, "11"), new Person(2, "22")
);
//如果年龄相同 就按照姓名排序
//以下两种写法输出一样
//第一种写法
people.stream().sorted((o1, o2) -> {
if(o1.getAge()-o2.getAge()==0){
return o1.getName().compareTo(o2.getName());
}else{
return o1.getAge()-o2.getAge();
}
}).forEach(System.out::println);
//第二种写法
System.out.println("---------------------------------------");
//多属性排序 也和sql查询相同 当第一个排序字段重复时候 第二个排序字段才会生效
people.stream().sorted(
Comparator.comparing(Person::getAge)
.thenComparing(Person::getName)
).forEach(System.out::println);
5.2注意compare不会改变原来的值
@AllArgsConstructor
@Getter
@ToString
public static class A{
int a;
int b;
int c;
}
public static void main(String[] args) {
List<A> as = Arrays.asList(new A(1, 9, 3), new A(1, 5, 6), new A(1, 2, 9));
//compare不会改变原来的值,要新写一个返回
List<A> collect = as.stream().sorted(Comparator.comparing(A::getA).thenComparing(A::getB).thenComparing(A::getC)).collect(Collectors.toList());
//输出新的返回值,是按照比较大小排列的
collect.forEach(System.out::println);
System.out.println("----------------------------------------------------");
//输出原来的数组,还是按照原来的方式排列的
as.forEach(System.out::println);
}
5.2.1运行结果
5.3引申到foreach是否会在循环中改变原list的值
foreach其实就是一个for循环嘛,for循环中能改变的东西,foreach中也能改变
六.收集到指定容器中
Collectors.toCollection
List<Person> people = Arrays.asList(
//字符串比较是按照字母顺序一个一个比较
new Person(3, "bbc"), new Person(3, "caa"),
new Person(5, "55"), new Person(6, "66"),
new Person(1, "11"), new Person(2, "66")
);
people.stream().map(Person::getName).collect(Collectors.toList()).forEach(System.out::println);
System.out.println("----------------------------------------------------------------");
//去重
people.stream().map(Person::getName).collect(Collectors.toSet()).forEach(System.out::println);
System.out.println("******************************************************************");
//收集到指定的容器中
people.stream().map(Person::getName).collect(Collectors.toCollection(TreeSet::new)).forEach(System.out::println);
七.计算
1.求平均值
Collectors.averagingInt
//求平均值
Double collect = people.stream().collect(Collectors.averagingInt(Person::getAge));
2.其他
//最大值
people.stream().max(Comparator.comparingInt(Person::getAge)).ifPresent(person -> System.out.println("max:"+person.getAge()));
//最小值
people.stream().min(Comparator.comparingInt(Person::getAge)).ifPresent(person -> System.out.println("min:"+person.getAge()));
//个数
System.out.println("count:"+people.stream().count());
//和
people.stream().reduce((person, person2) -> new Person(person.getAge()+person2.getAge(),"sum"))
.ifPresent(person -> System.out.println("sum:"+person.getAge()));
//和
as.stream().map(A::getA).reduce(Integer::sum).ifPresent(integer -> System.out.println("reduceA "+integer));
//平均值
System.out.println("avg:"+people.stream().collect(Collectors.averagingInt(Person::getAge)));
综上可见 .collect方法都要用到Collectos工具类
八.collectingAndThen对处理过后的stream再次处理
这个有难度
注意 Collectors.collectingAndThen是包含在Collectors.groupingBy里边的 并且是在分组之后的
1.Collectors.collectingAndThen一般以Collectors.toList()作为第一个入参
在idea中的写法,就是先Collectors.collectingAndThen()
然后第一个入参 Collectors.toList() 第二个入参 new 空格 回车就自动出现实现的函数了
2.分组后的数据 以sex为key 以分组得到的name集合为value
Map<String, List<String>> collect = people.stream().collect(Collectors.groupingBy(PersonWithSex::getSex,
Collectors.collectingAndThen(Collectors.toList(),
personWithSexes -> personWithSexes.stream().map(PersonWithSex::getName)
.collect(Collectors.toList()))));
3.分组后的数据 以sex为key 以对应的age和为value
Map<String, Integer> collect1 = people.stream().collect
(
Collectors.groupingBy
(PersonWithSex::getSex, Collectors.collectingAndThen(Collectors.toList(),
personWithSexes -> personWithSexes.stream().reduce
((personWithSex, personWithSex2) ->
new PersonWithSex(personWithSex.getAge() + personWithSex2.getAge(), "", "", 0))
.get().getAge()))
);
4.值得一看的博客
这位大佬对于CollectingAndThen 的解释很清晰,以下转自大佬博客
Collectors.collectingAndThen()_技匠而已的博客-CSDN博客_collectingandthen
Collectors.collectingAndThen() 函数应该最像 map and reduce 了,它可接受两个参数,第一个参数用于 reduce操作,而第二参数用于 map操作。
也就是,先把流中的所有元素传递给第一个参数,然后把生成的集合传递给第二个参数来处理。
例如下面的代码
把 [1,2,3,4] 这个集合传递给 v -> v * 2 lambda表达式,计算得出结果为[2,4,6,8]
然后再把 [2,4,6,8]传递给 Collectors.averagingLong 表达式,计算得出 5.0
然后传递给 s -> s * s lambda表达式,计算得到结果为 25.0
@Test
public void collectingAndThenExample() {
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Double result = list.stream().collect(Collectors.collectingAndThen(Collectors.averagingLong(v -> {
System.out.println("v--" + v + "--> " + v * 2);
return v * 2;
}),
s -> {
System.out.println("s--" + s + "--> " + s * s);
return s * s;
}));
System.out.println(result);
}
运行结果
v--1--> 2
v--2--> 4
v--3--> 6
v--4--> 8
s--5.0--> 25.0
25.0
九.拼接
Collectors.joining
1.普通拼接
String collect1 = people.stream().map(PersonWithSex::getName).collect(Collectors.joining());
System.out.println(collect1);
2.指定分隔符拼接
//指定分隔符拼接
String collect2 = people.stream().map(PersonWithSex::getName).collect(Collectors.joining(","));
System.out.println(collect2);
3.指定分隔符和前后缀拼接
//拼接时指定首位符号
String collect3 = people.stream().map(PersonWithSex::getName).collect(Collectors.joining(",","start[","]end"));
System.out.println(collect3);
4.数组转字符串经常用到
List<A> as = Arrays.asList(new A(1, 9, 3), new A(1, 5, 6), new A(1, 2, 9));
String collect = as.stream().map(a ->
String.valueOf(a.getB())).collect(Collectors.joining(","));
System.out.println("Collectors.joining(\",\")"+collect);
//顺便说下字符串转数组
List<String> list = Arrays.stream(collect.split(",")).collect(Collectors.toList());
九.一段有文化的代码
1.返回实体
使用了@Bulider和@Data注解后,就可以使用链式风格优雅地创建对象
@Getter
@Setter
@Builder//重点就是这个@Builder注解
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class SelectOption {
String value;
String name;
String tips;
}
2.枚举类
@Getter
@AllArgsConstructor
public enum RecognitionEnum {
TEST1("TEST1", "测试1"),
TEST2("TEST2", "测试2"),
TEST3("TEST3", "测试3");
String value;
String desc;
}
3.代码实现
public List<SelectOption> recognitionTypeOption() {
RecognitionType[] values = RecognitionEnum.values();//取出枚举数组
return Arrays.stream(values)
//通过map进行实体转换,从枚举实体转换为指定返回实体 这里用到.build链式赋值
.map(recognitionType -> SelectOption.builder().name(recognitionType.getDesc()).value(recognitionType.getValue()).build())
.collect(Collectors.toList());//最终转换为list返回
}