1. Lambda表达式
1.1 含义
百度百科
lambda expression是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数 。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)
java官方例子
链接:https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
个人理解
在java中的lambda表达式可以看做是一个匿名方法,但java中并没有匿名方法这一说法,只有匿名类,当匿名类实现的接口只有一个抽象方法时,那么这个匿名类就可以写作lambda表达式,所以lambda表达式实际上是一个接口的匿名实现类。
1.2 @FunctionalInterface
含义
@FunctionalInterface是一个注解,用于标记一个接口是函数式接口,表明这个接口只有一个抽象方法,多个的情况下编译器会报错。
满足条件
满足加注解的条件就是看接口中抽象方法的数量,必须是1个。
接口中的方法都是没有abstract关键字修饰的抽象方法,但是从java8开始中接口是可以有default方法和static方法的。所以这两种是不计入数量的。
还有另一种情况,接口中和Object中同名的方法也是不计入的。
作用
函数式接口的实现可以用lambda表示。其实当一个接口满足加@FunctionalInterface的条件,但没有加时,也可以用lambda表示。
例子
@FunctionalInterface
public interface MyFunction {
void test();
String toString();
}
public interface MyFunction2 {
void test();
String toString();
}
public static void main(String[] args) {
MyFunction m = () -> {};
MyFunction2 m2 = () -> {};
}
1.3 基本语法
格式
- (parameters) -> expression
- (parameters) ->{ statements; }
含义及解释
- parameters是参数列表,参数的类型可以不指定,编译器会自动推断。
- parameters只有一个参数时,可以不用()包起来,如果没有参数或多个时,则必须用()包起来。
- expression是一个表达式,可以不用{}包裹,statements是语句列表,如果只包含1条时,也可以不用{}。
例子
public interface M1 {
void test(int i);
}
public interface M2 {
int test(int i);
}
M1 m1 = (a) -> {System.out.println(a);};
M1 m11 = a -> {System.out.println(a);};
M1 m12 = a -> System.out.println(a);
M1 m13 = a -> {System.out.println(a);System.out.println(a);};
2. 方法引用
2.1 含义
java8新增::符号用于表示方法引用, 是对lambda表达式更简洁的一种写法,是lambda表达式的一种语法糖。
2.2 语法
类型 | 语法 | 等价的Lambda表达式 |
---|---|---|
构建方法引用 | ClassName::new | () -> new ClassName() |
实例方法引用 | instance::instanceMethod | () -> instance.instanceMethod() |
静态方法引用 | ClassName::staticMethod | () -> ClassName.staticMethod() |
对象方法引用 | ClassName::instanceMethod | (arg1,arg2[,…]) -> arg1.instanceMethod(arg2[,…]) |
2.3 例子
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static Student getInstance() {
return new Student();
}
public Student get() {
return new Student();
}
public int compare(Student student) {
return this.name.compareToIgnoreCase(student.name);
}
}
@FunctionalInterface
public interface MyFunction {
Student get();
}
public interface MyFunction2 {
int compare(Student s1, Student s2);
}
public static void main(String[] args) {
// 等价于 MyFunction m1 = () -> new Student();
MyFunction m1 = Student::new;
// 等价于 MyFunction m12 = () -> Student.getInstance();
MyFunction m12 = Student::getInstance;
Student student = new Student();
// 等价于 MyFunction m13 = () -> student.get();
MyFunction m13 = student::get;
// 等价于 MyFunction2 m2 = (ma, mb) -> ma.compare(mb);
MyFunction2 m2 = Student::compare;
String[] stringArray = { "Barbara", "James", "Mary", "John",
"Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
}
3 几个重要的函数式接口
这几个接口都位于java.util.function包下面,是java官方提供、很常用的函数式接口,对理解Stream API很有用处
3.1 Consumer
@FunctionalInterface
public interface Consumer<T> {
// 接收一个参数,没有输出
void accept(T t);
// 先执行this, 再执行after
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Consumer<String> strPrintln = System.out::println;
strPrintln.accept("1");
strPrintln.andThen(strPrintln).accept("hello");
3.2 Function
Stream中使用map的参数就是Function
@FunctionalInterface
public interface Function<T, R> {
// 输入一个参数,输出一个参数
R apply(T t);
// 先执行before,再执行this
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
// 先执行this,再执行after
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
// 原样输出
static <T> Function<T, T> identity() {
return t -> t;
}
}
Function<Integer, Integer> f = a -> a * a;
Function<Integer, Integer> f1 = a -> a + a;
System.out.println(f.apply(1));
System.out.println(f.compose(f1).apply(1));
System.out.println(f.andThen(f1).apply(1));
// 输出
// 1
// 4
// 2
3.3 Predicate
Stream中使用filter的参数就是Predicate
@FunctionalInterface
public interface Predicate<T> {
// 输入一个参数,返回布尔值
boolean test(T t);
// 逻辑与操作,
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
// 逻辑非
default Predicate<T> negate() {
return (t) -> !test(t);
}
// 逻辑或
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
Predicate<String> p = str -> str.length() >= 5;
Predicate<String> p1 = str -> str.length() <= 10;
String testWord = "HelloWorld";
System.out.println(p.test(testWord));
System.out.println(p.and(p1).test(testWord));
System.out.println(p.negate().test(testWord));
System.out.println(p.or(p1).test(testWord));
// 输出
// true
// true
// false
// true
3.4 Supplier
@FunctionalInterface
public interface Supplier<T> {
// 不接受参数,返回1个结果,常用于工厂方法
T get();
}
Supplier<LocalDate> date = LocalDate::now;
System.out.println(date.get());
4. Stream
Stream是对集合(Collection)对象功能的增强
4.1 Api
-
allMatch
集合转化为流,流中所有元素都要匹配。传入参数Predicate
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" }; boolean b = Arrays.stream(stringArray).allMatch(str -> str.length() > 8 && str.length() < 20); System.out.println(b); Predicate<String> p1 = str -> str.length() > 8; Predicate<String> p2 = str -> str.length() < 20; boolean c = Arrays.stream(stringArray).allMatch(p1.and(p2)); System.out.println(c);
-
anyMatch
流中任一元素匹配就返回true,否则false。传入参数Predicate
Stream<String> stream = Arrays.stream(stringArray); boolean b = stream.anyMatch(str -> str.length() > 10); System.out.println(b);
-
collect
将流中元素收集起来。传入参数Collector,返回集合
List<String> stringList = Arrays.stream(stringArray).collect(Collectors.toList()); System.out.println(stringList); Set<String> stringSet = Arrays.stream(stringArray).collect(Collectors.toSet()); System.out.println(stringSet);
-
concat
静态方法,将两个流连接为一个流
Stream<String> concat = Stream.concat(Arrays.stream(stringArray), Arrays.stream(stringArray));
-
count
计算流中元素的个数
long count = Arrays.stream(stringArray).count();
-
distinct
对流中的元素去重,使用 Object.equals方法
long count = Arrays.stream(stringArray).distinct().count();
-
filter
过滤容器中的元素,只保留函数返回true的元素。参数为Predicate,返回Stream
List<String> stringList = Arrays.stream(stringArray).filter(str -> str.length() > 8 && str.length() < 20).collect(Collectors.toList());
-
findAny
流中任一元素匹配就返回它,多次调用可能不是同一个结果,是不稳定的
Optional<String> any = Arrays.stream(stringArray).findAny(); any.ifPresent(System.out::println);
-
findFirst
流第一个匹配的元素,多次调用返回同一个结果,是稳定的
Optional<String> any = Arrays.stream(stringArray).findFirst(); any.ifPresent(System.out::println);
-
flatMap
map的扁平化操作,将流中每个元素转化为流,并合并到一起
Arrays.stream(stringArray).flatMap(str -> Stream.of(str.split(""))).collect(Collectors.toList()).forEach(System.out::println);
-
forEach
对流中的每个元素进行操作,是没有输出的,传入一个Consumer.在并行流中不能保证元素顺序
Arrays.stream(stringArray).collect(Collectors.toList()).forEach(System.out::println);
-
forEachOrdered
和forEach类似,不过在并行流中能保证顺序
-
generate
静态方法,生成1个无限无序的流,常和limit用来组合
Stream.generate(() -> 0).limit(5).forEach(System.out::println); Stream.generate(new Random()::nextInt).limit(5).forEach(System.out::println); Stream.generate(() -> new Random().nextInt(5)).limit(5).forEach(System.out::println);
-
iterate
静态方法,生成1个无限有序的流,常和limit用来组合
Stream.iterate(0, n -> n + 2).limit(5).forEach(System.out::println); Stream.iterate("0", n -> n + "0").limit(5).forEach(System.out::println);
-
limit
限制流中元素个数
-
map
将集合中的元素转化为另一种类型的元素,输入参数为Function
Arrays.stream(stringArray).map(String::length).forEach(System.out::println);
-
mapToInt
和map类似,如果使用map返回的类型是Integer, 后续操作需要拆装箱,可以使用这个来提高性能
List<Integer> list = Arrays.asList(1,2,3,4); // 流中的元素都是对象,这里对List中Integer进行加法计算,需要频繁拆箱和装箱 int sum1 = list.stream().reduce(0,(acc,e) -> acc + e).intValue(); // 使用mapToInt可以减少拆装箱次数,提高性能 int sum2 = list.stream().mapToInt(e -> e).sum();
-
max
返回流中最大值, 可传入一个Comparator
Arrays.stream(stringArray).max(String::compareTo).ifPresent(System.out::println); Arrays.stream(stringArray).max(Comparator.comparingInt(String::length).reversed()).ifPresent(System.out::println);
-
min
返回流中最小值
-
noneMatch
对流中元素进行匹配,都不满足返回true,空流也返回true
boolean n = Arrays.stream(stringArray).noneMatch(s -> s.length() > 20); boolean empty = Arrays.stream(stringArray).filter(s -> s.charAt(0) == ' ').noneMatch(s -> s.length() > 20);
-
of
静态方法,将数组组装为Stream
-
peek
peek返回流中的元素,额外的操作就是对流中元素执行传入的函数。传入参数Consumer
这个方法的存在主要是为了支持调试,元素流经管道中的某个点时能看到
Stream.of("one", "two", "three", "four") .filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList()).forEach(System.out::println);
-
reduce
对流中元素进行reduce(减少,缩小)操作,将所有元素处理得到唯一值。reduce有三个方法,从以下三个例子可以看出reduce的用法。
-
Optional<T> reduce(BinaryOperator<T> accumulator)
// 累加,由于流可能为空,最后会得到一个Optional Optional<Integer> resultOptional = Stream.of(1, 2, 3).reduce((acc, e) -> acc + e); // 等价于 boolean foundAny = false; Integer result = null; int [] array = {1, 2, 3}; BinaryOperator<Integer> accumulator = (acc, e) -> acc + e; for (Integer e : array) { if (!foundAny) { foundAny = true; result = e; } else result = accumulator.apply(result, e); } Optional<Integer> resultOptional = foundAny ? Optional.of(result) : Optional.empty();
-
T reduce(T identity, BinaryOperator<T> accumulator)
// 同样是累加,存在初始值 int result = Stream.of(1, 2, 3).reduce(0, (acc, e) -> acc + e); // 等价于 int [] array = {1, 2, 3}; int identity = 0; BinaryOperator<Integer> accumulator = (acc, e) -> acc + e; int result = identity; for (Integer e : array) result = accumulator.apply(result, e);
-
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
combiner在并行流的情况下生效,将不同线程操作的结果汇总在一起。其余和两个参数的reduce方法一致
int result = Stream.of(1, 2, 3).parallel().reduce(0, Integer::sum, (a, b) -> {System.out.println("parallel combiner invoke"); return a + b;}); int other = Stream.of(1, 2, 3).reduce(0, Integer::sum, (a, b) -> {System.out.println("combiner invoke"); return a + b;});
-
-
skip
丢弃流中前n个元素
Arrays.stream(stringArray).skip(3L).collect(Collectors.toList()).forEach(System.out::println);
-
sorted
对流中元素进行排序
Arrays.stream(stringArray).sorted().collect(Collectors.toList()).forEach(System.out::println); Arrays.stream(stringArray).sorted(Comparator.comparingInt(String::length)).collect(Collectors.toList()).forEach(System.out::println);
-
toArray
将流中元素转化为一个数组
-
collect
-
Collector
在流中的数据经常要使用collect方法收集起来,传递给collect的参数是Collector。jdk提供了一些常用的实现在Collectors类中,比如Collectors.toList()、Collectors.toSet()。
public static <T> Collector<T, ?, List<T>> toList() { return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add, (left, right) -> { left.addAll(right); return left; }, CH_ID); }
-
collect
collect还有个方法,提供和reduce类似的三个参数
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
- supplier 创建一个新容器,并行可能会创建多次,并且每次不同对象
- accumulator 将元素添加进结果集中
- combiner 在并行计算时汇总不同线程计算的结果。它的输入是两个结果集,必须将第二个结果集中的值全部放入第一个结果集中
List<String> asList = Arrays.stream(stringArray).collect(ArrayList::new, ArrayList::add, ArrayList::addAll); List<String> asList1 = Arrays.stream(stringArray).collect(() -> {System.out.println("new invoke"); return new ArrayList<>();}, ArrayList::add, (a, b) -> {System.out.println("invoke");}); List<String> asList2 = Arrays.stream(stringArray).parallel().collect(() -> {System.out.println("new invoke"); return new ArrayList<>();}, ArrayList::add, (a, b) -> {System.out.println("invoke");});
-
-
groupingby
groupingby提供和数据库group by相类似的操作,对流中数据进行分组。使用collect会生成Map<K, List>这种的Map对象,groupingby还可以进行二次分组,对每个已分好组的K中的List再进行分组
Map<Boolean, List<String>> collect = Arrays.stream(stringArray).collect(Collectors.groupingBy(s -> s.length() > 5)); System.out.println(collect); Map<Integer, List<String>> collect1 = Arrays.stream(stringArray).collect(Collectors.groupingBy(String::length)); System.out.println(collect1); Map<Boolean, Map<Integer, List<String>>> collect2 = Arrays.stream(stringArray).collect(Collectors.groupingBy(s -> s.length() > 5, Collectors.groupingBy(String::length))); System.out.println(collect2);
还可以使用mapping对List进行处理
Map<Boolean, Set<String>> collect2 = Arrays.stream(stringArray).collect(Collectors.groupingBy(s -> s.length() > 5, Collectors.mapping(String::toUpperCase, Collectors.toSet()))); System.out.println(collect2);