- 基本结构 (arguments) -> body
- 参数类型可推导时,不需指定类型: (a,b) -> a + b
- 当仅有一个参数且类型可推导时,不强制写 () : a -> a + 1
- 参数指定类型,必须有括号: (Integer i) -> i + 1
- 参数可为空: () -> "hello, lambda"
- body需要用 {} 包含语句,当仅有一条语句时可省略
- lambda表达式与匿名内部类的区别
- |
lambda表达式 |
匿名内部类 |
类型不同 |
只支持单抽象方法的接口 |
接口、抽象类、具体类 |
是否需要实例化 |
否 |
需要 |
内存分配 |
只需分配一次内存,存储在堆的永久区中 |
类被新建时,需要给对象分配内存 |
实现原理 |
编译后,无单独的字节码文件,对应字节码在运行时动态生成 |
编译后,生成单独的字节码文件 |
一、函数式接口
函数式接口:只有一个抽象方法的接口,目的是为了某一个单一的操作
函数式接口声明,在接口上添加注解 @FunctionalInterface
- JDK8之前已存在被标注为函数式接口的接口
- java.lang.Runnable
- java.util.Comparator
- java.util.concurrent.Callable
- java.io.FileFilter
- java.security.PrivilegedAction
- java.beans.PropertyChangeListener
- JDK8新增的函数式接口(基于java.util.function包下)
- Function:函数,T -> R
- BiFunction:函数,(T,U) ->R
- Predicate:断言/判断,T -> boolean
- BiPredicate:断言/判断,(T,U) -> boolean
- Supplier:生产者,() -> T
- Consumer:消费者,(T) -> ;
- BiConsumer:消费者,(T,U) ->;
- UnaryOperator:单元运算,(T) -> T
- BinaryOperator:二元运算,(T,T) -> T
- BooleanSupplier, DoubleBinaryOperator, DoubleConsumer, DoubleFunction, DoublePredicate, DoubleSupplier, DoubleToIntFunction, DoubleToLongFunction, DoubleUnaryOperator, IntBinaryOperator, IntConsumer, IntFunction, IntPredicate, IntSupplier, IntToDoubleFunction, IntToLongFunction, IntUnaryOperator, LongBinaryOperator, LongConsumer, LongFunction, LongPredicate, LongSupplier, LongToDoubleFunction, LongToIntFunction, LongUnaryOperator, ToDoubleBiFunction, ToDoubleFunction, ToIntBiFunction, ToIntFunction, ToLongBiFunction, ToLongFunction等
0、总结
-
如无参数,请使用Supplier(Use Supplier if it takes nothing)
-
如无返回,请使用Consumer(Use Consumer if it returns nothing)
-
如两者都无,请使用Runnable(Use Runnable if it does neither)
-
如两者都有,请使用Function(Use Function if it does both)
-
如返回布尔值,请使用Predicate(Use Predicate if it returns a boolean)
-
如以上皆不可以,请使用自定义@FunctionalInteface(Use @FunctionalInteface if none of above works)
1、快速使用
1)简化匿名函数式接口实现
函数式接口:只包含一个抽象方法声明的接口
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia"); Collections.sort(names, new Comparator<String>() { @Override public int compare(String a, String b) { return b.compareTo(a); } });
- 写法
// 写法1:简化匿名类写法 Collections.sort(names, (String a, String b) -> { return b.compareTo(a) }); // 写法2:在1的基础上,省略方法参数类型 Collections.sort(names, (a, b) -> { return b.compareTo(a) }); // 写法3:在2的基础上,单行方法省略return和{} Collections.sort(names, (a, b) -> b.compareTo(a));
2)方法和构造函数引用
- Converter接口
@FunctionalInterface interface Converter<F, T> { T convert(F from); }
- 写法
//原始写法 Converter<String, Integer> converter0 = new Converter<String, Integer>() { @Override public Integer convert(String from) { return Integer.valueOf(from); } }; //lamada表达式写法:(from) -> Integer.valueOf(from)返回一个接口实现类 Converter<String, Integer> converter1 = (from) -> Integer.valueOf(from); Integer converted1 = converter1.convert("123"); // 更进一步的简化写法 Converter<String, Integer> converter2 = Integer::valueOf; Integer converted2 = converter2.convert("123");
- 实现Person类构造函数的引用
class Person { String firstName; String lastName; Person() {} Person(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } } interface PersonFactory<P extends Person> { P create(String firstName, String lastName); } PersonFactory<Person> personFactory = Person::new; Person person = personFactory.create("Peter", "Parker");
3) lambda访问范围
- 局部变量
- 外部变量在lambda内部使用时,会在编译时隐式声明final修饰
int num = 1; // 等同于final int num = 1; Converter<Interge, String> converter = (from) String.valueOf(from + num);
- 成员变量与静态成员变量
- 支持在lambda内部读写操作
public class VarTest { int num; static staticNum; @Test public void test() { Converter<Integer, String> converter = (from) -> { num = 1; return String.valueOf(from + num); }; Converter<Integer, String> staticConverter = (from) -> { staticNum = 2; return String.valueOf(from + staticNum); }; } }
- default接口方法
- 编译不支持
2、内置函数式接口
1. Predicate<T>
- 布尔类型函数,处理复杂逻辑判断,test方法为执行方法
-
- and(Predicate p):与其他Predicate对象组成全满足关系
- or(Predicate p):与其他Predicate对象组成满足任一即可关系
- negate():结果取反
// 判断整数是否在[60,100]区间内 Predicate<Integer> predicate = i -> i >= 60 && i <= 100; // test方法最终执行逻辑判断 predicate.test(10); // false predicate.test(100); // true
2. Function<T, R>
- 接收单一参数返回单一结果,apply方法为执行方法
-
- andThen(Function f):下一个执行Function
- compose(Function f):在此之前执行Function
Function<Integer, Integer> add = x -> x + 10; Function<Integer, Integer> mul = x -> x * 2; add.andThen(mul).apply(3); // (3 + 10) * 2 = 26
3. Supplier<T>
- 获得一个给定类型的结果,无入参
Supplier<Integer> supplier = () -> 1; supplier.get(); // 1
4. Consumer<T>
- 接收单一入参无返回结果
-
- andThen(Consumer c):链式追加
Consumer<Person> consumer = p -> { p.setAge(p.getAge() + 1); } consumer.accept(new Person("Test", 1));
5. Comparators
- 接收两个同类型参数,比较大小
List<Person> list = ListUtils.list(new Person("a", 33), new Person("d", 15), new Person("b", 22), new Person("e", 15)); // 按age降序,再按name倒序 list.sort(Comparator.comparing(Person::getAge) .thenComparing(Person::getName).reversed());
6. Optionals
- 存储一个对象值,解决NPE问题
// 传统写法 if (user == null) { doSomething(); } // 优雅写法 Optional.ofNullable(user).ifPresent(u -> doSomething()); // orEles-不论Optional对象是否含空值,都会执行调用 // orElseGet-则只会在Optional对象含空值时调用 Optional.ofNullable(null).orElse(createNewUser()); Optional.ofNullable(null).orElseGet(createNewUser()); // filter 结果为true则返回对象,否则返回含空值的Optional Optional.ofNullable(new User("a")) .filter(u -> StringUtils.equals("a", user.getName())); // orElseThrow 存在值则返回,否则抛出异常 Optional.ofNullable(null) .orElseThrow(() -> { // do something return new RuntimeException("NPE"); }); // map:对实例值操作,并封装为Optional返回 // flatMap:对实例值操作,不封装Optional返回 Optional.ofNullable("jack") .map((s) -> s.toUpperCase()) .orElse("NPE"); Optional.ofNullable("jack") .map((s) -> Optional.ofNullable(s.toUpperCase())) .orElse("NPE");
二、方法引用
1、快速使用
- 语法
-
- 构造方法引用: Type::new
- 数组构造方法引用:Type[]::new
- 静态方法引用:Type::methodName
- 实例对象的实例方法引用:instanceName::methodName
- 实例类型的实例方法引用:Type::methodName
// 方法引用写法 Function<String, Integer> function = Integer::new; // lambda写法 function = str -> new Integer(str); // 传统写法 function = new Function<String, Integer>() { public Integer apply(String str) { return new Integer(str); } } // 数组构造方法引用 Function<Integer, String[]> function = String[]::new; // lambda写法 function = length -> new String[length]; // 传统写法 function = new Function<Integer, String[]>() { public String[] apply(Integer length) { return new String[length]; } } // 静态方法引用 Function<Integer, String[]> function = String[]::new; // lambda写法 function = length -> new String[length]; // 传统写法 function = new Function<Integer, String[]>() { public String[] apply(Integer length) { return new String[length]; } } // 实例对象的实例方法引用 List list = Arrays.asList("bikaqiu", "bikabika", "bika"); Predicate<String> predicate = list::contains; // 实例类型的实例方法引用 Function<String, Integer> function = String::length;
2、方法引用
1. 构造方法引用
语法: Type::new
// 整数String转换为Integer // 方法引用写法 Function<String, Integer> f = Integer::new; // lambda写法 f = s -> new Interger(s); // 传统写法 f = new Function() { public Integer apply(String s) { return new Integer(s); } }
2. 数组构造方法引用
语法: Type[]::new
// 构造指定长度的String数组 // 方法引用写法 Function<Integer, String[]> f = String[]::new; // lambada f = length -> new String[length];
3. 静态方法引用
// lambda写法 Function<String,Integer> f = s -> Integer.parseInt(s); // 方法引用写法 Function<String,Integer> f = Integer::parseInt;
4. 实例方法引用
// 判断List中是否存在指定String List<String> names = Arrays.asList(new String[]{"Tom", "summery"}); Predicate check = names::contains; check.test("Tom"); // true
5. 类型方法引用
// 输出String的长度 Function<String, Integer> f = String::length; System.out.println(f.apply("TomCat")); // 输出List中每个String元素的长度 List<String> names = Arrays.asList(new String[]{"Tom", "summery"}); names.stream().map(String::length).forEach(System.out::println); // 切割字符串 BiFunction<String, String, String[]> sp = String::split; String[] apply = sp.apply("this.is.Test,sdjsk.sdfav", "\\.");
三、Stream
JDK8引入的新特性, java.util.stream 包中,是一组支持串行并行聚合操作的元素,即集合/迭代器的增强版
- stream():串行执行流对象操作
- paralleStream():并行执行流对象操作
- Arrays.stream(T array):将数组转换为流对象操作
- 特性
- 单次处理,即一次处理完毕后,当前Stream关闭
- 支持并行操作
- 流的基本步骤
- 获取数据源
- 数据转换:每次转换原有Stream对象不改变,会返回新Stream对象
- 执行操作获取结果
1、方法
1. forEach
- 流中止操作,遍历每个元素
List<String> list = Arrays.asList("B", "C", "H", "A"); // 简写list.stream().forEach(System.out::println); list.stream().forEach(e -> System.out.println(e));
2. filter
- 流中间操作,接收一个Predicate对象,对每一个元素进行过滤,常用配合collect或其他操作
// 过滤过于大于50和小于0的元素,取最大值 List<Integer> list = Arrays.asList(40, 2, 33, 55, 32, 14, 5, 10, -10, -33); Integer max = list.stream().filter(i -> i <= 50 && i >= 0).max(Integer::compareTo).get();
3. sorted
- 流中间操作,对集合进行排序并返回流对象;该操作不改变原集合对象顺序,仅返回一个排序过的流对象视图
List<String> list = Arrays.asList("B", "C", "H", "A"); // 默认自然排序 list.stream().sorted().forEach(System.out::println); // 降序 list.stream().sorted(Comparator.reverseOrder()).forEach(System.out::println); // 自定义排序 list.stream().sorted(Comparator.comparing(String::toString)).forEach(System.out::println);
4. map
- 流中间操作,将流中的每个元素对应到另一个对象中
List<String> list = Arrays.asList("Bob", "Car", "Height", "ALICE");
# 将字符串变为全小写输出
list.stream().map(s -> s.toLowerCase(Locale.CHINA)).forEach(System.out::println);
5. match
- 流中止操作,判断某一种规则是否与流对象中的元素匹配,返回Boolean值
List<String> list = Arrays.asList("Bob", "Car", "Height", "ALICE"); // 判断元素中是否任一满足首字符为B list.stream().anyMatch(s -> s.startsWith("B")); // true // 判断元素中是否都满足首字符为B list.stream().allMatch(s -> s.startsWith("B")); // false // 判断元素中是否都不满足首字符为B list.stream().noneMatch(s -> s.startsWith("B")); // false
6. count
- 流中止操作,返回当前流对象中元素数目
List<String> list = Arrays.asList("Bob", "Car", "Height", "ALICE"); // 满足开头首字符为B或A的元素数目 list.stream().filter(s -> s.startsWith("B") || s.startsWith("A")).count(); // 2
7. reduce
- 规约,可作为累加器、累乘器
// Optional<T> reduce(BinaryOperator<T> accumulator); Arrays.asList(1, 2, 3).stream().reduce((a, b) -> a + b).get()); // 6 // T reduce(T identity, BinaryOperator<T> accumulator); 相对上一个使用多了一个初始值设置 Arrays.asList(1, 2, 3).stream().reduce(0, (a, b) -> a + b)); // 6
8. concat
- 流连接
// 连接两个流并打印 Stream.concat(Stream.of(1, 2), Stream.of(3)).forEach(System.out::print); // 123
9. peek
- 生成包含原Stream流所有元素的新Stream流,且新Stream流每个元素被消费前执行peek给定的消费函数
// 打印元素前先打印元素-1的值 Stream.of(2, 4).peek(x -> System.out.print(x - 1)).forEach(System.out::print); // 1234
10. max/min
- 获取最大或最小元素
- 存在多个最值相同的元素,则返回首个最值元素
List<Integer> list = Arrays.asList(40, 2, 33, 55, 32, 14, 5, 10); Integer max = list.stream().max(Integer::compareTo).get();
11. limit
- 截取前N个元素
// 截取前四位取最小值 List<Integer> list = Arrays.asList(40, 2, 33, 55, 32, 14, 5, 10, -10, -33); list.stream().limit(4).forEach(System.out::println);
12. collect
将处理后的Stream流组装成集合
// 过滤负数元素 List<Integer> list = Arrays.asList(33, -10, 60 ,-4, 6, 80 , -33); List<Integer> collect = list.stream().filter(i -> i >= 0).collect(Collectors.toList()); // User[id,name,age] // 将List<User>转换为Map<id, name> userList.stream().collect(Collectors.toMap(User::getId, User::getName)); // 将List<User>转换为Map<id, User> userList.stream().collect(Collectors.toMap(User::getId, User -> User)); userList.stream().collect(Collectors.toMap(User::getId, Function.identity())); // List<User>存在key冲突问题转换 userList.stream().collect(Collectors.toMap(User::getId, Function.identity(), (k1, k2) -> k2));
注意点(踩坑)
- 使用Collectors.toMap(key, value)时,要注意key不能重复,否则默认的合并策略会抛出异常提示重复KEY:Duplicate key
- 源码
public static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) { return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new); } private static <T> BinaryOperator<T> throwingMerger() { return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }; }
- 解决办法:手动指定合并策略,走重载方法
List<JSONObject> list = new ArrayList<>(10); list.add(buildJSONObject("1", "n1")); list.add(buildJSONObject("1", "n2")); list.add(buildJSONObject("2", "n3")); list.stream .collect(Collectors.toMap(d -> d.getString("id"), d -> d, (oldValue, newValue) -> newValue));
- 使用该方法返回Map时,要避免Value为NULL值,否则在合并时会抛出NPE异常
- 源码
public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier) { BiConsumer<M, T> accumulator = (map, element) -> map.merge(keyMapper.apply(element), valueMapper.apply(element), mergeFunction); return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID); } // Map.merge() default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); // 此处就是Value为NULL时抛出NPE的地方 V oldValue = get(key); V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); if(newValue == null) { remove(key); } else { put(key, newValue); } return newValue; }
- 解决方式:在collect调用前使用filter过滤Value为NULL的情况;或者Value回写Map时做判断NULL初始化
list.stream
.filter(Objects::nonNull)
.collect(Collectors.toMap(d -> d.getString("id"), d -> d, (oldValue, newValue) -> newValue));
mapToInt/mapToLong/mapToDouble
- 汇总数据对象:IntSummaryStatistics, DoubleSummaryStatistics, LongSummaryStatistics
- 对应方法:getCount(总数)、getMax(最大值)、getMin(最小值)、getAverage(平均值)
List<Integer> list = Arrays.asList(33, -10, 60 ,-4, 6, 80 , -33);
IntSummaryStatistics summary = list.stream().mapToInt(Integer::intValue).summaryStatistics();
2、用法
- 遍历索引
List<String> list = Arrays.asList("a", "b", "c", "d"); Stream.iterate(0, i -> i + 1).limit(list.size()).forEach(i -> { System.out.println(i + ":"+ list.get(i)); });
- 分组统计
/* [Person{c='A', d=10, e=75}, Person{c='B', d=24, e=61}, Person{c='D', d=16, e=20}, * Person{c='A', d=17, e=99}, Person{c='C', d=20, e=67}, Person{c='D', d=10, e=14}, Person{c='B', d=4, e=41}, Person{c='A', d=29, e=35}, Person{c='A', d=6, e=61}, Person{c='D', d=4, e=42}] */ // 按C进行分组,并对D汇总 Map<String, Integer> d = list.stream() .collect(Collectors.groupingBy(Person::getC, Collectors.summingInt(Person::getD))); // {A=62, B=28, C=20, D=30} // 按C进行分组,并对E汇总(不支持BigDecimal,使用reducing解决) Map<String, BigDecimal> e = list.stream().collect(Collectors.groupingBy(Person::getC, Collectors.reducing(BigDecimal.ZERO, Person::getE ,BigDecimal::add))); // {A=270, B=102, C=67, D=76} // 一次返回以分组统计好的通类型对象 List<Person> collect = group.entrySet().stream().map(entry -> { Person p = new Person(); p.setC(entry.getKey()); p.setD(entry.getValue().stream().map(Person::getD).reduce(Integer::sum).orElse(0)); p.setE(entry.getValue().stream().map(Person::getE).reduce(BigDecimal.ZERO, BigDecimal::add)); return p; }).collect(Collectors.toList()); // [Person{c='A', d=75, e=304}, Person{c='B', d=4, e=17}, Person{c='D', d=36, e=255}]