目录
背景
一个简单的lambda表达式例子
函数式接口
谓词逻辑
基础操作总结
遍历
map
flatMap与map
查找与匹配
元素连接joining
规约reduce
Optional
流操作过程的理解
收集器Collectors
分组收集groupingBy
聚合操作/统计
有状态操作与无状态操作
limit(n)
skip(n)
distinct()
去重拓展
sorted()
排序拓展
并行流
背景
个人复习总结笔记,可能有点乱吼,后续学习继续完善,白话+自己的理解
一个简单的lambda表达式例子
public interface Demo {
void test(String a);
}
个人复习总结笔记,可能有点乱吼,后续学习继续完善,白话+自己的理解
public interface Demo {
void test(String a);
}
一个功能接口作为参数进行传递的简化(原始->lambda)
public class TestMethod {
public static void main(String[] args) {
//原始:一个功能接口的实现
Demo demo = new Demo() {
@Override
public void test(String a) {
System.out.println(a);
}
};
//简化
Demo demo1 = (String a) ->{
System.out.println(a);
};
//进一步简化
Demo demo2 = a -> System.out.println(a);
//TestMethod testMethod = new TestMethod();
//testMethod.print("test",a -> System.out.println(a));
TestMethod testMethod = new TestMethod();
testMethod.print("test",demo);
}
//通过传入不同的Demo接口的实现完成不同的功能
public void print(String a,Demo demo){
demo.test(a);
}
}
集合List,数组,文件流能用Stream
集合list.stream
数组Stream.of(array)
文件流
此处接口中如果存在多个方法,则不能使用lambda表达式,因为不知道会调用哪个方法,故考虑
函数式接口
特点:只有一个抽象方法(没有方法的实现),可定义静态非抽象,可定义默认非抽象(带有函数体)方法default(实现类可以不用实现),可以不带@FunctionalInterface,允许存在java.lang.Object中的public方法,如equals
谓词逻辑
谓语,用来修饰限定主语
如Predicate 用于校验的谓词逻辑,当一个判断逻辑多出地方用到,就可以提取出来
简单理解为一个lambda表达式抽取出来
多个谓词逻辑共同成立 用.and()连接 表示共同成立,其他连接词根据业务逻辑来定
如 .or()或 .negate() 取相反的类似非
Listusers = Arrays.asList(new User(12,"M"),new User(null,"M")); // List user2 = users.stream().filter(e -> { // if(e.getAge() == null){ // e.setAge(100); // return true; // } // return e.getAge() != null && e.getAge() > 10; // }).collect(Collectors.toList()); List user2 = users.stream().filter(predicateTest()).collect(Collectors.toList()); user2.stream().forEach(System.out::print); } static Predicate predicateTest() { return e -> { if(e.getAge() == null){ e.setAge(100); return true; } return e.getAge() != null && e.getAge() > 10; }; }
谓词逻辑可无限套娃
public class Test {
public static void main(String[] args) {
List users = Arrays.asList(new User(12,"M"),new User(11,"W"));
// List user2 = users.stream().filter(e -> {
// if(e.getAge() == null){
// e.setAge(100);
// return true;
// }
// return e.getAge() != null && e.getAge() > 10;
// }).collect(Collectors.toList());
List user2 = users.stream().filter(predicateTestAll()).collect(Collectors.toList());
user2.stream().forEach(System.out::print);
}
static Predicate predicateTest() {
return e -> {
if(e.getAge() == null){
e.setAge(100);
return true;
}
return e.getAge() != null && e.getAge() > 10;
};
}
static Predicate predicateTest2() {
return e -> e.getSex().equals("M") ;
}
static Predicate predicateTestAll() {
return predicateTest().and(predicateTest2());
}
}
基础操作总结
遍历
forEach 输出流中的元素
list.stream().forEach(System.out::println);
map
1.map 将流中的元素从一种格式转换为另外一种格式(类型)如 Integer ->String
stream.map(e -> Integer..parseInt(e))
2.mapToInt 将元素转为整型
Stream.of("a","b").mapToInt(String::length)
3.map与foreach有时候可以完成一样的功能,但是map操作如果是复杂操作的话(值得是没用方法引用),需要用return返回最终的结果元素,foreach不用。
//map
list.stream.map(e -> {
e.setAge(1);
return e;
}).collect(Collectors.toList());
//foreach
list.foreach(e -> {
e.setAge(1);
})
flatMap与map
flatMap理解为,处理多个集合合并,或者一个元素处理后变成一个数组,最终结果是集合的集合(List>),数组中有数组,管道中有管道等多维的情况,此时要合成一个简单一维的字符串数组或者直接输出,此时单单map就有点复杂
Listsss = Arrays.asList("abb","sss"); List ss2 = sss.stream().map(e -> e.split("")).collect(Collectors.toList()); ss2.stream().forEach(System.out::print);
此时打印出两个数组,但不是需要的值
[Ljava.lang.String;@6267c3bb[Ljava.lang.String;@533ddba
如果不用java8输出,那么需要两个for循环,采用java8 map的话就不能嵌套循环输出,所以此时考虑用flatMap扁平化(理解,把不同集合的元素 拍进同一个流中)
进一步简单理解为:flatMap 将集合的集合中的,集合元素转为流进行操作,然后合并多个流,汇聚成最终流。
Listssss = Arrays.asList("abb","sss"); ssss.stream().flatMap(e -> Arrays.stream(e.split(""))) .forEach(System.out::print); //List s = ssss.stream().flatMap(e -> //Arrays.stream(e.split(""))).collect(Collectors.toList());
查找与匹配
匹配
anyMatch() 是否存在至少一个符合要求的元素 存在返回true,不存在返回false
boolean result = list.stream().anyMatch(e -> e.getAge > 10);
allMatch() 是否所有元素都符合条件 是true 反之false
noneMatch() 是否不存在符合条件的元素
查找
findFirst() 查找第一个元素
Optionalresult = list.stream().filter(e -> e.getAge > 10).findFirst(); User u = result.get();
isPresent() 是否存在
ifPresent() 如果存在做什么
orElse() 如果不存在做什么
元素连接joining
流中元素用指定符号连接或直接连接
//所有元素用逗号连接成字符串
String s = list.stream().collect(Collectors.joining(","));
规约reduce
目的:将一个集合转换为一个对象(如Integer,String等)
举例:
将集合中的元素累加,得出最终值
*reduce(初始值,累计操作器,合并器)
reduce(初始值,lambda表达式) 其中lambda表达式(a,b) -> {} 其中a表示当前结果,b为元素
累加
Integer sum = list.stream().reduce(0,(temp , e) -> temp + e); //或 Integer sum2 = list.stream().reduce(0,Integer::sum);
字符串拼接
String str = list.stream().reduce("",String::concat);
当设计到parallelStream()并行流时,reduce将有第三个参数,为合并器,将分组的结果进行合并
如此处将元素 1 2 3 4 分为两组1+2,3+4进行计算,最后通过合并器将两组结果合并为最终结果
Integer sum = list.parallelStream().reduce(0,Integer::sum,Integer::sum);
Optional
个人理解:一个包装类,将结果进行包装,防止空指针异常
如果Optional包装后的对象,值存在,调用isPresent()方法返回true,调用get()方法返回该对象, 如果不存在抛异常
流操作过程的理解
流操作的过程可以分三部分(通俗比喻:冰川水入海流)
(1)源操作:河流源头,雪山、冰川、湖泊等 -》 数组,集合,行文本文件
(2)中间操作:河流河床经过的一些地方及人们对河流的影响及操作 -》(无状态操作)filter,map,flatmap,(有状态操作)limit,distinct,skip,sorted等
(3)终端操作:入海 -》.collect(Collectors.asList());
.collect(Collectors.toSet());
收集器Collectors
//收集为集合
.collect(Collectors.asList());
//收集为set
.collect(Collectors.toSet());
//收集成通用集合
.collect(Collectors.toCollection(linkedList::new));
.collect(Collectors.toCollection(linkedHashSet::new));
.collect(Collectors.toCollection(PriorityQueue::new));
//收集为数组
.toArray(String[]::new);
//收集为map 指定键值
.collect(Collectors.toMap(User::getId,User::getName));
//指定键,值为对象本身
.collect(Collectors.toMap(User::getId,User->User));
.collect(Collectors.toMap(User::getId, Function.identity()));//效果同上
//map中重复key的解决方法
//第二个key覆盖第一个key 或者 事先将充当key值得元素去重等
.collect(Collectors.toMap(User::getId, Function.identity(),(key1,key2)->key2));
分组收集groupingBy
(1)根据某一个字段值分组,并统计
//根据年龄分组,统计各个年龄对应多少人 Mapm = list.stream() .collect(Collectors.groupingBy(User::getAge,Collectors.counting()));
(2)多次分组嵌套
//先按班级分组,再按性别分组,统计每班男女各多少人 Map> m = list.stream() .collect(Collectors.groupingBy(User::getClass, Collectors.groupingBy(User::getSex,Collectors.counting())));
(3)自定义key值得分组
//统计已成年,未成年分别多少人,键为已成年,未成年 Mapm = games.stream().collect(Collectors.groupingBy(e -> { if(e.getAge() > 18 ){ return "已成年"; }else { return "未成年"; },Collectors.counting()));
(3)键为日期字符串,对键进行排序最后有序存入map中
//数据源,键为日期字符串 Mapmap = Maps.newHashMap(); map.put("1-25",4L); map.put("1-21",3L); map.put("1-22",4L); //时间排序且结果有序 Map result = Maps.newlinkedHashMap(); map.entrySet().stream().sorted(Map.Entry.comparingByKey()) .forEachOrdered(e -> result.put(e.getKey(),e.getValue())); //打印结果 result.entrySet().forEach(System.out::println);
聚合操作/统计
count() 计数
//通常与过滤一起使用 long count = list.stream().count();
sum()求和
int sum = list.stream().sum();
average()求平均值
OptionalDouble average = list.stream().average();
max()/min() 最大值和最小值
int max = list.stream.max();
有状态操作与无状态操作
对于中间操作 有状态无状态的理解
无状态操作是对本身进行操作比如自身转换,过滤,与别人无关
有状态则需要参照前后元素位置改变等,如对比后排序,跳过,对比去重
limit(n)
取前n个元素
skip(n)
跳过前n个取后面元素
distinct()
去重 -》衍生自定义去重
去重拓展
sorted()
自然排序 -》衍生自定义排序
排序拓展
集合的常规排序
以前集合通过Collections.sort()来进行排序,通过传递排序规则来进行自定义排序
如list.sort(String.CASE_INSENSITIVE_ORDER) //不考虑大小写按字母表顺序排序
倒序排序:list.sort(Comparator.comparing(User::getAge)).reversed());
多个排序规则:list.sort(Comparator.comparing(User::getAge).thenComparingInt(User::getId));
java8 sorted自定义排序
前提:流中元素必须实现Comparable接口
(1) 自然升序 一般用于List
list.stream().sorted().collect(Collectors.toList());
(2)根据条件升序 List
list.stream().sorted(Comparator.comparing(User::getAge)).collect(Collectors.toList());
(3)自然降序
ist.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
(4)根据条件降序
list.stream().sorted(Comparator.comparing(User::getAge).reversed()).collect(Collectors.toList());
(5)多个字段排序
list.sorted(Comparator.comparing(User::getAge).thenComparing(User::getId)).collect(Collectors.toList());
(6)含空值排序
如果排序条件含有null的,可以排到最后,不处理的话会报空指针异常
list.stream().sorted(Comparator.comparing(User::getAge,Comparator.nullsLast(String::compareTo))).collect(Collectors.toList());
并行流
1.流的串行(sequential()默认)与并行(parallel())
之前的操作都为串行,加上.parallel()变为并行流
串行流:一个个处理,保证输入与输出顺序对应
并行流:并行处理,速度更快,但顺序改变(注意有状态的操作会收到影响,因为分组并行,与最终整体结果的处理会不一致)
不适合用并行流的情况:linkedList链表(区别ArrayList有下标方便并行时分组),阻塞队列,IO流等。适合:ArrayList,HashMap,数组
运行效率对比:
不能简单对比,并行流与cpu资源有关,且数据量越大,Stream流的执行效率越高,所以一般情况可能运行的速度 并行流大于for循环大于可能等于串行流
parallelStream() 与stream.parallel() 相同
2.使用并行流时候forEach()与forEachOrdered()的区别
forEach() 由于并行流分组处理,故输出不保证顺序
forEachOrdered() 按照元素输入的顺序进行输出
(后续完善更新中...)



