1.Lambda表达式
可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表,如下所示简单的Lamdda表达式:
2.函数式接口
函数式接口定义且只定义了一个抽象方法,需要使用@FunctionalInterface,个标注用于表示该接口会设计成一个函数式接口
函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作函数描述符
常用函数式接口
函数式接口 | 函数描述符 | 原始类型特化 |
---|---|---|
Predicate | T->boolean | IntPredicate,LongPredicate, DoublePredicate |
Consumer | T->void | IntConsumer,LongConsumer, DoubleConsumer |
Function<T,R> | T->R | IntFunction,IntToDoubleFunction,IntToLongFunction, LongFunction,LongToDoubleFunction, LongToIntFunction,DoubleFunction, ToIntFunction,ToDoubleFunction,ToLongFunction |
Supplier | ()->T | BooleanSupplier,IntSupplier, LongSupplier,DoubleSupplier |
UnaryOperator | T->T | IntUnaryOperator,LongUnaryOperator, DoubleUnaryOperator |
BinaryOperator | (T,T)->T | IntBinaryOperator,LongBinaryOperator, DoubleBinaryOperator |
BiPredicate<L,R> | (L,R)->boolean | |
BiConsumer<T,U> | (T,U)->void | ObjIntConsumer,ObjLongConsumer, ObjDoubleConsumer |
BiFunction<T,U,R> | (T,U)->R | ToIntBiFunction<T,U>,ToLongBiFunction<T,U>, ToDoubleBiFunction<T,U> |
3.方法引用
先前:
inventory.sort((Apple a1, Apple a2)-> a1.getWeight().compareTo(a2.getWeight()));
使用方法引用和 java.util.Comparator.comparing:
inventory.sort(comparing(Apple::getWeight)); //使用“::”这个来调用方法,
一般没有对原始数据做其他特殊处理,可以用方法引用的更直观简单,如果没办法还是得用Lambda表达式。
4.使用流
4.1.流主要是对集合的操作处理
filter:谓词筛选
foreach:输出每一个元素
distinct:筛选掉相同元素
limit(n):截断流,获取指定的前n个数据
skip(n):跳过集合前n个元素
map:表示映射元素,集合中的一个元素映射到一个新的元素
flatMap:扁平化集合,可以把两个集合合并成一个集合
allMatch:流中是否有一个元素能匹配给定的谓词
anyMatch:流中的元素是否都能匹配给定的谓词
noneMatch:它可以确保流中没有任何元素与给定的谓词匹配
findFirst:将返回当前流中的任意元素
findAny:找到当前流中的第一个元素
reduce:规约数据(对每一个元素做累积值,可以求和,积,求最大值,最小值,可以给定初始值,也可以不给定初始值)
count:计算流中元素的个数
sorted:排序
如下具体的详情描述:
flatMap例子一:
String[] words = {"Goodbye", "World"}; //如下通过flatMap可以获取这个数组中包含的所有字符(不重复)
List<String> uniqueCharacters =words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
flatMap例子二:
//如下可以让两个数组的数自由的组合
List<Integer> numbers1 = Arrays.asList(1, 2, 3);
List<Integer> numbers2 = Arrays.asList(3, 4);
List<int[]> pairs =numbers1.stream()
.flatMap(i -> numbers2.stream().map(j -> new int[]{i, j}))
.collect(toList());
reduce带初始值例子三:
int sum = numbers.stream().reduce(0, Integer::sum);
reduce不带初始值例子三:
Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));
4.2.流转换
1)映射到基本类型流
mapToInt:映射基本类型int的流, IntStream 还支持其他的方便方法,如max 、 min 、 average 等
mapToDouble:映射基本类型double的流
mapToLong:映射基本类型long的流
2)转换回为对象流
boxed:相当于对基本类型的装箱
mapToObject:这个也可以实现
3)数值范围
range,rangeClosed
这两个方法都是第一个参数接受起始值,第二个参数接受结束值。但
range 是不包含结束值的,而 rangeClosed 则包含结束值
例如:
IntStream.rangeClosed(1, 100) //这个范围是包含100的,结果的数据有50个
.filter(n -> n % 2 == 0)
IntStream.range(1, 100)
.filter(n -> n % 2 == 0) //这个范围是不包含100的,结果的数据只有49个
4.3 创建流
1)使用Stream.of创建
Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
2)由数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
3)文件流
如下通过Files.lines创建流
long uniqueWords = 0;
try(Stream<String> lines =Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
}
catch(IOException e){
}
4)创建无限流
Stream API提供了两个静态方法来从函数生成流: Stream.iterate 和Stream.generate 。 这两个操作可以创建所谓的无限流:不像从固定集合创建的流那样有固定大小的流。由 iterate
和 generate 产生的流会用给定的函数按需创建值,因此可以无穷无尽地计算下去!一般来说,应该使用 limit(n) 来对这种流加以限制,以避免打印无穷多个值。
i) iterate 方法要接受一个 UnaryOperator 作为参数,表示不停的迭代上一个数
例子一:
//Stream.iterate 如下是生成2,4,6,8...总共10个偶数,都是前一个数加2所得
Stream.iterate(0, n -> n + 2)
.limit(10)
.forEach(System.out::println);
例子二:
//Stream.iterate 可以用来生成斐波那契函数
Stream.iterate(new int[]{0, 1},t -> new int[]{t[1],t[0] + t[1]})
.limit(10)
.map(t -> t[0])
.forEach(System.out::println);
//这段代码将生成斐波纳契数列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34…
ii) generate 方法也可让你按需生成一个无限流。但 generate 不是依次对每个新生成的值应用函数的。它接受一个 Supplier 类型的Lambda提供新的值。
例子一:
Stream.generate(Math::random)
.limit(5)
.forEach(System.out::println);//生成新的5个函数
例子二:
//同样生成斐波那契函数,不过这个存在有中间状态,在并行条件下是不安全的,不建议这样用
IntSupplier fib = new IntSupplier(){
private int previous = 0;
private int current = 1;
public int getAsInt(){
int oldPrevious = this.previous;
int nextValue = this.previous + this.current;
this.previous = this.current;
this.current = nextValue;
return oldPrevious;
}
};
IntStream.generate(fib).limit(10).forEach(System.out::println);
5.收集流
收集流可以对流使用collect方法,然后使用Collector类提供的方法进行收集。
它们主要提供了三大功能:
a. 将流元素归约和汇总为一个值
b. 元素分组
c. 元素分区
方法 | 说明 |
---|---|
Collector.toList() | 将流转换为一个List集合 |
Collector.toSet | 将流转换为一个Set集合,不包含重复元素 |
Collector.toSet | 将流收集起来成为集合 |
Collectors.counting() | 可以返回流中有多少个元素 |
Collectors.maxBy(Comparator) | 可以返回流里最大的元素(某个条件) |
Collectors.minBy(Comparator) | 可以返回流里最小的元素(某个条件) |
Collectors.summingInt(Function) | 对于流中的元素求和,返回int,同样summingDouble则是返回double类型 |
Collectors.averagingDouble(Function) | 对流中的元素求平均值返回double类型,同样有averagingInt,averagingLong |
Collectors.joining() | 对CharSequence的元素流进行组合,可以使用带参数做分割,以及前后添加元素 |
Collectors.reducing (U identity,Function<? super T, ? extends U> mapper,BinaryOperator op) | 上面所有的都是这个reducing的特殊情况,都可以通过这个方法实现,这个有三个重载方法。 |
Collectors.groupingBy(Function) | 对于流通过某一个元素进行分组,这个也支持嵌套分组,返回Map集合,第二个参数可以对生成的集合进行求和,数量计算,最大,最小等 |
Collectors.collectingAndThen | 把结果集转换为另外一个结果,一般可以对包装的Optional进行get提取元素 |
Collectors.partitioningBy(Predicate) | 对流进行分区,和GroupBy相似,不过只分为true和false两组;他和可以和groupingBy嵌套使用 |
6.流中插入动作peek
peek 的设计初衷就是在流的每个元素恢复运行之前,插入执行一个动作。但是它不像 forEach 那样恢复整个流的运行,而是在一个元素上完成操作之后,它只会将操作顺承到流水线中的下一个操作。