目录
目录
ofNullable()创建允许为null的Optional对象
isPresent()判断Optional对象是否存在,存在返回true,不存在返回false
背景
个人复习总结笔记,可能有点乱吼,后续学习继续完善,白话+自己的理解
一个简单的lambda表达式例子
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表达式,因为不知道会调用哪个方法,故考虑
函数式接口
特点
只有一个抽象方法(没有方法的实现)的接口就为函数式接口;
函数式接口可以通过 lambda 表达式、方法引用、构造方法引用来创建实例;
接口带注解@FunctionalInterface则强制为函数式接口;
可定义静态非抽象方法,可定义非抽象(带有函数体)默认方法default(实现类可以不用实现);
可以不带@FunctionalInterface,允许存在java.lang.Object中的public方法,如equals;
四大函数式接口
Function函数型接口
输入值,返回处理后的值,T输入类型,R返回类型
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
......
}
举例
Function<String,Integer> function = s -> s.length();
//输出 4
System.out.println(function.apply("test"));
Predicate(判断型接口)
输入一个值进行判断,返回布尔类型
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
......
}
举例
Predicate<String> predicate = s -> s.length > 10;
//输出false
System.out.println(predicate.test("test"));
Consumer消费型接口
传入值,无返回值
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
......
}
举例
Consumer<String> consumer = s -> System.out.println(s);
//输出test
consumer.accept("test");
Supplier提供型接口
无传入值,但有输出值
@FunctionalInterface
public interface Supplier<T> {
T get();
}
举例
Supplier<String> supplier = ()->{return "test";};
//输出test
System.out.println(supplier.get());
谓词逻辑
谓语,用来修饰限定主语
如Predicate 用于校验的谓词逻辑,当一个判断逻辑多出地方用到,就可以提取出来
简单理解为一个lambda表达式抽取出来
多个谓词逻辑共同成立 用.and()连接 表示共同成立,其他连接词根据业务逻辑来定
如 .or()或 .negate() 取相反的类似非
List<User> users = Arrays.asList(new User(12,"M"),new User(null,"M"));
// List<User> 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<User> user2 = users.stream().filter(predicateTest()).collect(Collectors.toList());
user2.stream().forEach(System.out::print);
}
static Predicate<User> 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<User> users = Arrays.asList(new User(12,"M"),new User(11,"W"));
// List<User> 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<User> user2 = users.stream().filter(predicateTestAll()).collect(Collectors.toList());
user2.stream().forEach(System.out::print);
}
static Predicate<User> predicateTest() {
return e -> {
if(e.getAge() == null){
e.setAge(100);
return true;
}
return e.getAge() != null && e.getAge() > 10;
};
}
static Predicate<User> predicateTest2() {
return e -> e.getSex().equals("M") ;
}
static Predicate<User> 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扁平化流
flatMap处理多个集合且合并,或者一个元素处理后变成一个数组,最终结果是集合的集合(List<List<String>>),数组中有数组,管道中有管道等多维的情况,此时要合成一个简单一维的字符串数组或者直接输出,此时单单map就有点复杂
List<String> sss = Arrays.asList("abb","sss");
List<String[]> 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 将集合中的集合元素转成多个流,再进行操作,然后合并这些流,合成最终流。
List<String> ssss = Arrays.asList("abb","sss");
ssss.stream().flatMap(e -> Arrays.stream(e.split("")))
.forEach(System.out::print);
//List<String> 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()
查找第一个元素
Optional<User> result = 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及相关操作
理解
一个包装类,可以为null的容器对象,将结果进行包装,防止空指针异常。如果Optional包装后的对象,值存在调用isPresent()方法返回true,调用get()方法返回该对象, 如果不存在抛异常
empty()创建空的Optional对象
Optional<String> o = Optional.empty();
of()创建非空的Optional对象
此时of中的参数不能为null,否者也会抛出异常
Optional<String> o = Optional.of("test");
ofNullable()创建允许为null的Optional对象
Optional<String> o = Optional.ofNullable(null);
isPresent()判断Optional对象是否存在,存在返回true,不存在返回false
Optional<String> o = Optional.of("test");
Boolean s = o.isPresent();
ifPresent() 如果存在后做什么
Optional<String> o = Optional.of("test");
o.ifPresent(s -> System.out.println(s.length()));
ifPresentOrElse 如果存在做什么,否则做什么
Optional<String> o = Optional.of("test");
o.ifPresentOrElse(s -> System.out.println(str.length()),
() -> System.out.println("对象为空"));
orElse()如果对象为null做什么
String s = Optional.ofNullable(null).orElse("test");
orElseGet() 对象为null根据表达式做什么
String s = Optional.ofNullable(null)
.orElseGet(()->"test");
get()获取对象值
存在问题:当对象值为null,则会抛异常,所以一般用orElseGet()来获取
Optional<String> o = Optional.ofNullable("test");
System.out.println(o.get());
filter() 过滤
符合条件返回对象,不符合返回空Optional对象
Optional<String> o = Optional.ofNullable("test");
Boolean result = o.filter(e -> e.length() > 1).isPresent();
map()转换值
生成新的对象,原值不变
Optional<String> o1 = Optional.of("test");
Optional<Integer> o2 = o1.map(String::length);
流操作过程的理解
流操作的过程可以分三部分(通俗比喻:冰川水入海流)
源操作
河流源头,雪山、冰川、湖泊等 -》 数组,集合,行文本文件
中间操作
河流河床经过的一些地方及人们对河流的影响及操作 -》(无状态操作)filter,map,flatmap,(有状态操作)limit,distinct,skip,sorted等
终端操作
入海 -》.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
根据某一个字段值分组,并统计
//根据年龄分组,统计各个年龄对应多少人
Map<String,Long> m = list.stream()
.collect(Collectors.groupingBy(User::getAge,Collectors.counting()));
多次分组嵌套
//先按班级分组,再按性别分组,统计每班男女各多少人
Map<String,Map<String,Long>> m = list.stream()
.collect(Collectors.groupingBy(User::getClass,
Collectors.groupingBy(User::getSex,Collectors.counting())));
自定义key值得分组
//统计已成年,未成年分别多少人,键为已成年,未成年
Map<String,Long> m =
games.stream().collect(Collectors.groupingBy(e -> {
if(e.getAge() > 18 ){
return "已成年";
}else {
return "未成年";
},Collectors.counting()));
键为日期字符串顺序排序
对键进行排序最后有序存入map中
//数据源,键为日期字符串
Map<String,Long> map = Maps.newHashMap();
map.put("1-25",4L);
map.put("1-21",3L);
map.put("1-22",4L);
//时间排序且结果有序
Map<String,Long> result = Maps.newLinkedHashMap();
map.entrySet().stream().sorted(Map.Entry.comparingByKey())
.forEachOrdered(e -> result.put(e.getKey(),e.getValue()));
//打印结果
result.entrySet().forEach(System.out::println);
分组收集partitioningBy
通过条件表达式,将结果分为键为true,false两部分的map
//将年龄大于18岁的学生作为键是true的值,其他不符合条件的元素为键是false的值
Map<Boolean, List<User>> map =
list.stream().collect(Collectors.partitioningBy(e -> {
return e.getAge() > 18;
}));
聚合操作/统计
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();
IntSummaryStatistics的使用
List<Integer> nums = Arrays.asList(1,2,3,4,5,6); IntSummaryStatistics statistics = nums.stream().mapToInt(x -> x).summaryStatistics(); System.out.println(statistics.getMax());
getMax()最大值
getMin()最小值
getSum()和
getAverage()平均数
有状态操作与无状态操作
对于中间操作 有状态无状态的理解
无状态操作是对本身进行操作比如自身转换,过滤,与别人无关
有状态则需要参照前后元素位置改变等,如对比后排序,跳过,对比去重
limit(n)
取前n个元素
skip(n)
跳过前n个取后面元素
distinct()
去重 -》衍生自定义去重
去重拓展
通过对象中某个字段来去重,通过filter + map去重来实现
List<User> result = list.stream()
//通过map对数据进行去重,map.put方法如果键对应有值存在,则返回旧的值,塞入新的值,否则返回null
.filter(e -> map.put(e.getName(), e) == null)
.collect(Collectors.toList());
//提取出来
private static Predicate<User> userDistinct() {
final Map<String, User> map = new ConcurrentHashMap<>();
return e -> map.put(e.getName(), e) == null;
}
//或者使用Set,并发场景下考虑使用
private static Predicate<User> userDistinct2() {
final Set<String> set = new HashSet<>();
return e-> set.add(e.getName());
}
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<String>,List<Integer>
list.stream().sorted().collect(Collectors.toList());
(2)根据条件升序 List<User>,根据实体中某个字段
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() 按照元素输入的顺序进行输出
(后续完善更新中...)