- Lambda 表达式
格式
(parameters) -> expression
(parameters) -> { statements; }
伪代码如下
public static void main(String[] args) {
JFrame jFrame = new JFrame("My JFrame");
JButton jButton = new JButton("My button");
jButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
}
});
}
我们常常使用的匿名内部类如上
使用Lambda 表达式改写后如下
jButton.addActionListener(event -> System.out.println("button pressed"));
原理
@FunctionalInterface 函数式接口
如果接口只有一个抽象方法,那么它就是一个函数式接口
如果我们在某个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口
@FunctionalInterface
interface MyInterface{
void test();
String toString();
}
public class Test {
public void myTest(MyInterface myInterface){
System.out.println("进入myTest");
myInterface.test();
System.out.println("进入myTest执行完毕");
}
public static void main(String[] args) {
Test test = new Test();
test.myTest(()->{
System.out.println("输出mytest");
});
System.out.println("------------");
MyInterface myInterface =()->{
System.out.println("hello");
};
}
}
小结
即Java的函数式编程,其实只是把方法看作了一个对象,传入的还是一个接口,最终还是通过函数式接口去调用对应的方法,这也是java8为什么会有Default方法的原因之一,一个是为了兼容,子类不用去实现,就可以拥有方法的功能。
jdk8里面的函数式接口
JDK 1.8 新增加函数接口
统一在java.util.function包下面
接口 | 说明 |
---|---|
BiConsumer<T,U> | 代表了一个接受两个输入参数的操作,并且不返回任何结果。 |
BiFunction<T,U,R> | 代表了一个接受两个输入参数的方法,并且返回一个结果。 |
BinaryOperator<T> | 代表了一个作用于两个同类型操作符的操作,并且返回了操作符同类型的结果。 |
BiPredicate<T,U> | 代表了一个两个参数的 boolean 值方法。 |
BooleanSupplier | 代表了 boolean 值结果的提供方。 |
Consumer<T> | 代表了接受一个输入参数并且无返回的操作。 |
DoubleBinaryOperator | 代表了作用于两个 double 值操作符的操作,并且返回一个 double 值的结果。 |
DoubleConsumer | 代表一个接受 double 值参数的操作,并且不返回结果。 |
DoubleFunction<R> | 代表接受一个 double 值参数的方法,并且返回结果。 |
DoublePredicate<R> | 代表一个拥有 double 值参数的 boolean 值方法。 |
DoubleSupplier | 代表一个 double 值结构的提供方。 |
DoubleToIntFunction | 接受一个 double 类型输入,返回一个 int 类型结果。 |
DoubleToLongFunction | 接受一个 double 类型输入,返回一个 long 类型结果。 |
DoubleUnaryOperator | 接受一个参数同为类型 double,返回值类型也为 double 。 |
Function<T,R> | 接受一个输入参数,返回一个结果。 |
IntBinaryOperator | 接受两个参数同为类型 int,返回值类型也为 int 。 |
IntConsumer | 接受一个 int 类型的输入参数,无返回值 。 |
IntFunction<R> | 接受一个 int 类型输入参数,返回一个结果 。 |
IntPredicate | 接受一个 int 输入参数,返回一个 boolean 的结果。 |
IntSupplier | 无参数,返回一个 int 类型结果。 |
IntToDoubleFunction | 接受一个 int 类型输入,返回一个 double 类型结果 。 |
IntToLongFunction | 接受一个 int 类型输入,返回一个 long 类型结果。 |
IntUnaryOperator | 接受一个参数同为类型 int,返回值类型也为 int 。 |
LongBinaryOperator | 接受两个参数同为类型 long,返回值类型也为 long。 |
LongConsumer | 接受一个 long 类型的输入参数,无返回值。 |
LongFunction<R> | 接受一个 long 类型输入参数,返回一个结果。 |
LongPredicate | 接受一个 long 输入参数,返回一个 boolean 类型结果。 |
LongSupplier | 无参数,返回一个结果 long 类型的值。 |
LongToDoubleFunction | 接受一个 long 类型输入,返回一个 double 类型结果。 |
LongToIntFunction | 接受一个 long 类型输入,返回一个int类型结果。 |
LongUnaryOperator | 接受一个参数同为类型 long,返回值类型也为 long。 |
ObjDoubleConsumer<T> | 接受一个 object 类型和一个 double 类型的输入参数,无返回值。 |
ObjIntConsumer<T> | 接受一个 object 类型和一个 int 类型的输入参数,无返回值。 |
ObjLongConsumer<T> | 接受一个 object 类型和一个 long 类型的输入参数,无返回值。 |
Predicate<T> | 接受一个输入参数,返回一个 boolean 结果。 |
Supplier<T> | 无参数,返回一个结果。 |
ToDoubleBiFunction<T,U> | 接受两个输入参数,返回一个 double 类型结果。 |
ToDoubleFunction<T> | 接受一个输入参数,返回一个 double 类型结果。 |
ToIntBiFunction<T,U> | 接受两个输入参数,返回一个 int 类型结果。 |
ToIntFunction<T> | 接受一个输入参数,返回一个 int 类型结果。 |
ToLongBiFunction<T,U> | 接受两个输入参数,返回一个 long 类型结果。 |
ToLongFunction<T> | 接受一个输入参数,返回一个 long 类型结果。 |
UnaryOperator<T> | 接受一个参数为类型 T,返回值类型也为 T。 |
方法引用
可以直接引用已有 Java 类或对象(实例)的方法或构造器。与 Lambda 联合使用,可以使语言的构造更紧凑简洁,减少冗余代码。
通过方法的名字来指向一个方法。
使用一对冒号 :: 。
只是一个语法糖,并不是所有方法都可以使用方法引用
有几种使用方式
构造器引用
语法为 Class::new,或者 Class< T >::new。
静态方法引用
语法为 Class::staticMethod。
特定类的任意对象的方法引用
语法为 Class::method。
特定对象的方法引用
语法为 instance::method。
默认方法
默认方法是接口可以有实现方法,而且不需要实现类去实现其方法。
public interface IOperation {
default void print(){
System.out.println("");
}
}
静态默认方法
接口可以声明(并且可以提供实现)静态方法。
public interface Operation {
default void printA(){
System.out.println("A");
}
// 静态方法
static void printB(){
System.out.println("B");
}
}
Stream
以一种声明的方式处理数据
语法类似于SQL,即描述式的
需要将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
List<Integer> transactionsIds = widgets.stream().filter(b -> b.getColor() == RED).sorted((x,y) -> x.getWeight() - y.getWeight()).mapToInt(Widget::getWeight).sum();
像 filter 这样只描述 Stream,最终不产生新集合的方法叫作惰性求值方法。而像 sum 这样最终会从 Stream 产生值的方法叫作及早求值方法。
在一个 Stream 操作中,可以有多次惰性求值,但有且仅有一次及早求值。
小结
Stream(流)是一个来自数据源的元素队列并支持聚合操作。
元素是特定类型的对象,形成一个队列。 Java 中的 Stream 并不会存储元素,而是按需计算。
数据源流的来源。 可以是集合,数组,I/O channel, 产生器 generator 等。
聚合操作类似 SQL 语句一样的操作, 如 filter,map,reduce,find,match,sorted 等。
中间操作(惰性求值)都会返回Stream,只有调用及早求值,才会返回最终结果
流的分类
有限长度,即有限流
无限长度,即无限流,一般结合limit进行配合使用
生成流的几种方式
of
其生成的 Stream 是有限长度的,Stream 的长度为其内的元素个数。
返回
Stream<Integer> integerStream = Stream.of(1, 2, 3);
Stream<String> stringStream = Stream.of("A");
generator
返回一个 无限 长度的 Stream,其元素由 Supplier 接口的提供。
1.在 Supplier 里是一个函数接口,只封装了一个 get() 方法,其用来返回任何泛型的值,该结果在不同的时间内,返回的可能相同也可能不相同,没有特殊的要求。
2.通常用于随机数、常量的 Stream,或者需要前后元素间维持着某种状态信息的 Stream。
3.把 Supplier 实例传递给 Stream.generate() 生成的 Stream,默认是串行(相对 parallel 而言)但无序的(相对 ordered 而言)。
Stream.generate(java.lang.Math::random);
iterate
iterate方法,其返回的也是一个无限长度的 Stream,与 generate 方法不同的是,其是通过函数 f 迭代对给指定的元素种子而产生无限连续有序 Stream。
Stream.iterate(1, item -> item + 1).limit(10).forEach(System.out::println);
empty
empty方法返回一个空的顺序 Stream,该 Stream 里面不包含元素项。
Stream.empty();
Collection 接口和数组的默认方法
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
// 获取对应的平方数
List<Integer> squaresList = numbers.stream().map( i -> i*i).collect(Collectors.toList());
在 Arrays 类,封装了一些列的 Stream 方法,不仅针对于任何类型的元素采用了泛型,更对于基本类型作了相应的封装,以便提升 Stream 的处理效率。
int id[] = new int[]{1, 2, 3, 4};
Arrays.stream(id).forEach(System.out::println);
流的常见中间操作(Intermediate)
concat
1.concat 方法将两个 Stream 连接在一起,合成一个 Stream。
2.若两个输入的 Stream 都是排序的,则新 Stream 也是排序的。
3.若输入的 Stream 中任何一个是并行的,则新的 Stream 也是并行的。
4.若关闭新的 Stream 时,原两个输入的 Stream 都将执行关闭处理
Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5)).forEach(System.out::println);
distinct
distinct 方法以达到去除掉原 Stream 中重复的元素,生成的新 Stream 中没有没有重复的元素。
Stream.of(1, 2, 3, 1, 2, 3).distinct().forEach(System.out::println);
filter
filter 方法用于通过设置的条件过滤出元素。
// 获取空字符串的数量
Stream.of("1", "2", "3", "4", "", "", "7", "", "").filter(string -> string.isEmpty()).count();
map
map 方法用于映射每个元素到对应的结果。
// 获取对应的平方数
Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).map(i -> i * i).collect(Collectors.toList());
flatMap
flatMap 方法与 map 方法类似,都是将原 Stream 中的每一个元素通过转换函数转换,不同的是,该换转函数的对象是一个 Stream,也不会再创建一个新的 Stream,而是将原 Stream的元素取代为转换的 Stream。
原来是多个,会打平为一个,而map是原来是多和,返回的还是多个
Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).flatMap(i -> i * i).collect(Collectors.toList());
peek
peek 方法生成一个包含原 Stream 的所有元素的新 Stream,同时会提供一个消费函数(Consumer实例),新 Stream 每个元素被消费的时候都会执行给定的消费函数,并且消费函数优先执行。
Stream.of(1, 2, 3).peek(x -> System.out.print(x));
skip
skip 方法将过滤掉原 Stream 中的前 N 个元素,返回剩下的元素所组成的新 Stream。
Stream.of(1, 2, 3,4,5) .skip(2) .forEach(System.out::println);
sorted
sorted 方法将对原 Stream 进行排序,返回一个有序列的新 Stream。
Stream.of(5, 4, 3, 2, 1).sorted().forEach(System.out::println);
流的常见最终操作
collect
通过 collect 收集器,应用 Collector 工具。
转换成其他集合
toList
List<Integer> collectList = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
toSet
Set<Integer> collectSet = Stream.of(1, 2, 3, 4).collect(Collectors.toSet());
toCollection
其接受的函数参数必须继承于 Collection。
TreeSet<Integer> collectSet = Stream.of(1, 2, 3, 4).collect(Collectors.toCollection(TreeSet::new));
toMap
备注:若 Stream 中重复值,导致 Map 中 key 重复,在运行时会报异常
java.lang.IllegalStateException: Duplicate key
public Map<Long, String> getIdNameMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, Account::getUsername));
}
其它
方法 | 说明 |
---|---|
averagingDouble | 求平均值,Stream 的元素类型为 double |
averagingInt | 求平均值,Stream 的元素类型为 int |
averagingLong | 求平均值,Stream 的元素类型为 long |
counting | Stream 的元素个数 |
maxBy | 在指定条件下的,Stream 的最大元素。 |
minBy | 在指定条件下的,Stream 的最小元素。 |
reducing | reduce 操作,操作可以实现从 Stream 中生成一个值,其生成的值不是随意的,而是根据指定的计算模型。 |
summarizingDouble | 统计 Stream 的数据(double)状态,其中包括 count,min,max,sum 和平均。 |
summarizingInt | 统计 Stream 的数据(int)状态,其中包括 count,min,max,sum 和平均。、 |
summarizingLong | 统计 Stream 的数据(long)状态,其中包括 count,min,max,sum 和平均。、 |
summingDouble | 求和,Stream 的元素类型为 double |
summingInt | 求和,Stream 的元素类型为 int |
summingLong | 求和,Stream 的元素类型为 long |
max | 最大值 |
min | 最小值 |
对结果的分组与限制
groupingBy
Map<Boolean, List<Integer>> collectGroup= Stream.of(1, 2, 3, 4).collect(Collectors.groupingBy(it -> it > 3));
limit
Stream.of(1, 2, 3,4,5).limit(2).forEach(System.out::println);
Optional
Optional 类的引入很好的解决空指针异常。
方法 | 说明 |
---|---|
static <T> Optional<T> empty() | 返回空的 Optional 实例。 |
boolean equals(Object obj) | 判断其他对象是否等于 Optional。 |
Optional<T> filter(Predicate<? super <T> predicate) | 如果值存在,并且这个值匹配给定的 predicate,返回一个 Optional 用以描述这个值,否则返回一个空的 Optional。 |
<U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper) | 如果值存在,返回基于 Optional 包含的映射方法的值,否则返回一个空的 Optional。 |
T get() | 如果在这个 Optional 中包含这个值,返回值,否则抛出异常:NoSuchElementException |
void ifPresent(Consumer<? super T> consumer) | 如果值存在则使用该值调用 consumer , 否则不做任何事情。 |
static <T> Optional<T> of(T value) | 返回一个指定非 null 值的 Optional。 |
static <T> Optional<T> ofNullable(T value) | 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。 |
T orElse(T other) | 如果存在该值,返回值, 否则返回 other |
T orElseGet(Supplier<? extends T> other) | 如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。 |
<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常 |