函数式接口
什么是?
- 被
@FunctionalInterface
注解了的接口类 - 接口只有一个抽象方法。
java.util.function
-
Function<T, R>
功能函数 map() -
Consumer<T>
消费函数 action() -
Supplier<T>
生产函数 collect() -
Predicate<T>
谓语判断函数 filter() -
包内其他接口说明
// 类型限制,入参类型固定,返回值固定 IntFunction<R> IntConsumer // 数量限制:两个参数在接口类加前缀:Bi Binary 二元,2个输入参数类型 BiConsumer<T, U> BiFunction<T, U, R> // Operator接口,入参和返回参数一样,一元和二元 UnaryOperator<T> extends Function<T, T> BinaryOperator<T> extends BiFunction<T,T,T>
其他函数式接口
-
Comparator<T>
比较器 -
Runnable
线程接口
如何用?
-
创建接口实现类
-
创建内部匿名类
-
使用Lambda表达式
Lambda表达式
什么是?
用来表示一个行为或传递代码,用来创造函数式接口的实例(对象),匿名内部类也可以完成同样的事情。
- 箭头标识
->
箭头标识:
() -> 表达式 , s -> { ... } , (a,b)-> {...}
方法引用
如果一个Lambda只是直接调用这个方法,那么还是用名称去调用,而不是描述如何调用它。仅仅涉及单一方法的Lambda语法糖。
-
双冒号标识
::
方法引用简化Lambda表达式。双冒号标识::,前面是目标引用,后面是方法名称,不需要括号,因为没有实际调用这个方法。 Integer::sum list::add String::length HashMap::new 示例: (Apple a) -> a.getWeight() 等效 Apple::getWeight (String s) -> System.out.println(s) 等效 System.out::println (str, i) -> str.substring(i) 等效 String::substring
Lambda | 等效的方法引用 |
---|---|
Reference to a static method | ContainingClass::staticMethodName |
Reference to an instance method of a particular object | containingObject::instanceMethodName |
Reference to an instance method of an arbitrary object of a particular type | ContainingType::methodName |
Reference to a constructor | ClassName::new |
如何用?
// JDK8之前 匿名内部类写法
new Thread(new Runnable(){// 接口名
@Override
public void run(){// 方法名
System.out.println("Thread run()");
}
}).start();
// JDK8之后 Lambda表达式写法
new Thread(() -> {
System.out.println("Thread run()");
}).start();// 省略接口名和方法名
// JDK8之前
list.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
// JDK8之后
list.sort((s1,s2) -> s1.length()-s2.length());
list.sort(comparing(String::length));
// -> 和 双冒号
List<String> list = Arrays.asList("aaaa", "bbbb", "cccc");
list.forEach(s -> System.out.println(s)); // lambda
list.forEach(System.out::println); // method reference
Stream流
什么是?
Stream接口,主要是对集合对象功能的增强。简单理解是某种数据源的视图,数据源不能是Map。
Stream跟函数式接口关系非常紧密,没有函数式接口stream就无法工作。stream只能被“消费”一次。
从有序集合生成流时,会保留原有的顺序。慎用并行BaseStream
的parallel()
如何用?
-
获取Stream
- 调用
Collection.stream()
或者Collection.parallelStream()
并行。 - 调用
Arrays.stream(T[] array)
方法 。Stream<T> of(T... values)
静态方法。
- 调用
-
Stream的操作
-
中间操作只会生成一个标记了该操作的新stream
-
结束操作会触发实际计算,计算完成后stream就会失效
-
-
Stream常用api
-
Stream map(Function<? super T,? extends R> mapper) 映射器(个数不变,类型可能改变)
-
Stream filter(Predicate<? super T> predicate) 过滤器
-
Stream sorted(Comparator<? super T> comparator) 排序器
-
Stream distinct()去重
-
void forEach(Consumer<? super T> action) 迭代器
-
Optional<T> reduce(BinaryOperator<T> accumulator) 从一组元素生成一个值。
-
<R, A> R collect(Collector<? super T, A, R> collector) 收集器,生成一个集合或Map对象。
-
boolean anyMatch(Predicate<? super T> predicate); 一个匹配
-
boolean allMatch(Predicate<? super T> predicate); 所有匹配
-
boolean noneMatch(Predicate<? super T> predicate); 没有匹配
-
Optional<T> findAny(); 查找任意一个 并行操作更快
-
Optional<T> findFirst(); 查找第一个
-
Stream的归集操作 reduce 和 collect
-
reduce操作,
max()
、min()
都是reduce操作-
源码分析
// reduce()最常用的场景就是从一堆值中生成一个值。 Optional<T> reduce(BinaryOperator<T> accumulator) // identity 初始值 T reduce(T identity, BinaryOperator<T> accumulator) // identity 初始值,accumulator: 累加器,,combiner 并行计算 <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner) // 求最大值 Optional<T> max(Comparator<? super T> comparator); Optional<T> min(Comparator<? super T> comparator);
-
使用示例
// 1. 找出最长的单词 Stream<String> stream = Stream.of("I", "am", "Strean", "reduceMethod"); String max = stream.reduce((s1,s2) -> s1.length()-s2.length() > 0 ? s1:s2).get(); String max = stream.reduce("",(s1,s2) -> s1.length()-s2.length() > 0 ? s1:s2); // 比较时慎用初始值 String max = stream.max((s1,s2) -> s1.length() - s2.length()).get(); // 2. 求出数之和 Stream<String> stream = Stream.of("I", "am", "Strean", "reduceMethod"); Stream<Integer> stream = Stream.of(1,2,3,4,5,6,7,8,9,10); int sum = stream.reduce(0,(a,b) -> a+b ); // 采用并发流求长度之和 List<String> list = Arrays.asList("I", "am", "Strean", "reduceMethod"); Stream<String> stream = list.stream();// 并发流:parallelStream() int lengthSum = stream.reduce(0, (sum, str) -> { System.out.println("sum:"+sum+",str:"+str); return sum+str.length();},(a,b) -> { System.out.println("a:"+a+",b:"+b); // 标准流式没有 return a+b; });
-
-
collect操作,终极武器
一般流先接map映射器方法,再来归集操作
-
Collector是为Stream.collect()方法量身打造的接口,而
Collectors
是产生Collector接口的工具类,包含了CollectorImpl
实现类。 -
源码分析
// Stream 的collect方法 <R, A> R collect(Collector<? super T, A, R> collector); // supplier目前容器生产方式,accumulator累加器,元素如何加到容器中 <R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
-
使用示例
List<String> list = Arrays.asList("I", "am", "Strean", "reduceMethod"); // 基础用法: List<String> newList = list.stream().filter(s -> s.length() >3 ).collect(Collectors.toList()); Set<String> newSet = list.stream().filter(s -> s.length() >3 ).collect(Collectors.toSet()); // 将list集合转成Map Map<String,Integer> map= list.stream().collect(Collectors.toMap(t->t, t -> t.length())); Map<String,Integer> map= list.stream().collect(Collectors.toMap(Function.identity(),String::legth));
-
Optional类
什么是?
java.util.Optional<T> 是一个容器类,代表一个值存在或不存在,不能被序列化。
如何用?
-
创建Optional对象
Optional<Student> optStu = Optional.empty();// 1.空对象 Optional<Student> optStu = Optional.of(student); // 2.非空值 Optional<Student> optStu = Optional.ofNullable(student); // 3.允许null值
-
常用API,支持链式操作。
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) public T orElse(T other) public T orElseGet(Supplier<? extends T> other) public void ifPresent(Consumer<? super T> consumer) public Optional<T> filter(Predicate<? super T> predicate) public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
时间日期类
Java8之前,Date类都是可变类。而Java8的Date和Time API提供 线程安全的不可变类且线程安全的。
-
LocalDate
,LocalTime
,LocalDateTime
,Year
,Duration
,Period
// 年月日 System.out.println("LocalDate.now():" + LocalDate.now()); // 2019-07-09 // 时分秒 System.out.println("LocalTime.now():" + LocalTime.now()); // 09:38:26.014 // 完整日期 System.out.println("LocalDateTime.now():" + LocalDateTime.now()); // 2019-07-09T09:38:26.015 // Duration是用来计算两个给定的日期之间包含多少秒,多少毫秒 LocalDate localDate = LocalDate.now(); // Period:两个给定的日期之间包含多少天,,多少月或者多少年。记住整数,年,月,日 Period period = Period.between(localDate, localDate.plus(30, ChronoUnit.DAYS)); System.out.println(period.getMonths());
-
DateTimeFormatter
LocalDateTime localDateTime = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss"); DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME; System.out.println("localDateTime格式化:" + dtf.format(localDateTime)); // 2019-07-09T10:54:20.661 DateTimeFormatter formatter = DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss"); System.out.println("formatter格式化:" + formatter.format(localDateTime)); // 2019-07-09 10:54:20 // String timeStr = "2019-07-09 10:48:05"; 如果使用这个parse会出现错误 formatter.parse(timeStr).get(ChronoField.DAY_OF_MONTH) // 9 DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME; String timeStr = "2019-07-09T11:02:01.840"; LocalDateTime time = LocalDateTime.parse(timeStr,dtf); /** * DateTimeFormatter won't parse dates with custom format "yyyyMMddHHmmssSSS" * https://bugs.openjdk.java.net/browse/JDK-8031085 */
Java8与重构
方法参数传递由值传递,转变为类(对象)传递,匿名类,Lambda,方法引用。这是:值参数化到行为参数化的转变。
为了因对不断变化的需求,行为参数化。抽象化,使代码更加灵活,后期维护代码更加方便!
总结
学习三部曲:什么是? what 如何用? how 为什么? why
函数式编程又称λ演算(lambda calculus),匿名函数。在编程语言中的函数通常指的是方法。从接口式编程,到函数式编程!
Java8 提倡函数式编程:
- 简少代码量:减少for...each 语句,实现类,匿名内部类。
- 增加代码清晰度:方法内直接写明操作行为,更加好维护
- 提高计算效率:让并行计算更加简单
网友评论