摘要
JDK:1.8.0_202
# 一:常用方法
# 1.1 forEachXxx
- forEach:集合的遍历
- forEachOrdered:功能同上,不同的是 forEachOrdered 是有顺序保证的,也就是对Stream中元素按插入时的顺序进行消费。当开启并行的时候,forEach 和 forEachOrdered 的效果就不一样了。
@Test
public void testForEach() {
IntStream.range(0, 5).forEach(System.out::print); // 01234
System.out.println("\n========================");
IntStream.range(0, 5).parallel().forEach(System.out::print); // 24013 - 这个结果每次运行都是随机的
System.out.println("\n========================");
IntStream.range(0, 5).parallel().forEachOrdered(System.out::print); // 01234
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 1.2 collect
将操作后的对象转化为新的对象
public void testCollect() {
Set<String> list = IntStream.range(0, 4).mapToObj(String::valueOf).collect(Collectors.toSet());
list.forEach(System.out::print); // 0123
}
1
2
3
4
2
3
4
# 1.3 filter
过滤,只有满足 Filter 表达式的数据就可以留下来,不满足的数据被过滤掉。
public void testFilter() {
List<String> list1 = IntStream.range(0, 5).mapToObj(String::valueOf).collect(Collectors.toList());
List<String> list2 = list1.stream().filter("2"::equals).collect(Collectors.toList());
list2.forEach(System.out::print); // 2
}
1
2
3
4
5
2
3
4
5
# 1.4 map/mapToXxx
类型转换
public void testMap() {
List<String> list1 = IntStream.range(0,6).mapToObj(String::valueOf).collect(Collectors.toList());
List<Integer> list2 = list1.stream().map(Integer::valueOf).collect(Collectors.toList());
List<Integer> list3 = list1.stream().mapToInt(Integer::valueOf).boxed().collect(Collectors.toList());
list2.forEach(System.out::print); // 012345
}
1
2
3
4
5
6
2
3
4
5
6
# 1.5 distinct
元素去重
public void testDistinct() {
List<String> list1 = IntStream.range(0, 7).mapToObj(String::valueOf).collect(Collectors.toList());
list1.addAll(IntStream.range(4, 7).mapToObj(String::valueOf).collect(Collectors.toList()));
System.out.println(list1.size()); // 10
// 过滤
List<String> list2 = list1.stream().distinct().collect(Collectors.toList());
System.out.println(list2.size()); // 7
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 1.6 sorted
排序
public void testSorted() {
List<Integer> list1 = IntStream.range(0, 8).boxed().collect(Collectors.toList());
// 等同于 .sorted(Comparator.naturalOrder()) 自然排序
list1.stream().sorted().forEach(System.out::print); // 01234567
System.out.println();
// 倒序
list1.stream().sorted(Comparator.reverseOrder()).forEach(System.out::print); // 76543210
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 1.7 groupingBy
根据字段进行分组
public void testGroupBy() {
List<Integer> list1 = IntStream.range(1, 10).boxed().collect(Collectors.toList());
list1.forEach(System.out::print); // 012345678
System.out.println();
Map<Integer, List<Integer>> list2 = list1.stream().collect(Collectors.groupingBy(s -> s % 3));
System.out.println(list2); // {0=[3, 6, 9], 1=[1, 4, 7], 2=[2, 5, 8]}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 1.8 findXxx
- findFirst:匹配到第一个满足条件的值就返回;
- findAny:获取Stream中的某个元素,如果是串行情况下,一般都会返回第一个元素,并行情况下就不一定了。
@Test
public void testFindFirst() {
List<Integer> list1 = IntStream.range(1, 11).boxed().collect(Collectors.toList());
// findFirst()
Optional<Integer> first = list1.stream().filter(i -> i % 3 == 0).findFirst();
if (first.isPresent()) {
System.out.println(first.get()); // 3
} else {
System.out.println("无数据");
}
// findAny() - 串行流
Optional<Integer> any1 = list1.stream().filter(i -> i % 3 == 0).findAny();
if (first.isPresent()) {
System.out.println(any1.get()); // 3
} else {
System.out.println("无数据");
}
// findAny() - 并行流
Optional<Integer> any2 = list1.stream().parallel().filter(i -> i % 3 == 0).findAny();
if (first.isPresent()) {
System.out.println(any2.get()); // 6
} else {
System.out.println("无数据");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 1.9 reduce
循环求值
public void testReduce() {
List<Integer> list1 = IntStream.range(0, 10).boxed().collect(Collectors.toList());
// 累加
System.out.println(list1.stream().reduce(Integer::sum).orElse(-1)); // 45
// peduce 第一个参数表基数,额外加10
System.out.println(list1.stream().reduce(10, Integer::sum)); // 55
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 1.9 reduce
可以在 peek 方法里面做任何没有返回值的事情,比如打印日志
public void testPeek() {
IntStream.range(0, 10).boxed().peek(System.out::print).collect(Collectors.toList()); // 0123456789
}
1
2
3
2
3
# 1.10 limit
限制输出值个数,入参是限制的个数大小
public void testLimit() {
List<Integer> list1 = IntStream.range(0, 10).boxed().collect(Collectors.toList());
System.out.println(list1.size()); // 10
System.out.println(list1.stream().limit(2).count()); // 2
}
1
2
3
4
5
2
3
4
5
# 1.11 Max/Min
max、min 方法,可以获取集合中最大、最小的对象
public void testMaxMin() {
List<Integer> list1 = IntStream.range(0, 10).boxed().collect(Collectors.toList());
System.out.println(list1.stream().max(Integer::compareTo).get()); // 9
System.out.println(list1.stream().min(Integer::compareTo).get()); // 0
}
1
2
3
4
5
2
3
4
5
# 1.12 concat
连接两个Stream,不改变其中任何一个Steam对象,返回一个新的Stream对象
@Test
public void testConcat() {
IntStream i1 = IntStream.range(0, 5);
IntStream i2 = IntStream.range(5, 10);
IntStream i3 = IntStream.concat(i1, i2);
i3.forEach(System.out::print); // 0123456789
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 1.13 limit
跳过前 n 条数据
@Test
public void testSkip() {
IntStream.range(0, 5).skip(3).forEach(System.out::print); // 34
}
1
2
3
4
2
3
4
# 1.14 flatMapToXxx
- flatMap:用在一些比较特别的场景下,当你的Stream是
Stream<String[]>、Stream<Set<String>>、Stream<List<String>>
这几种结构时,需要用到 flatMap 方法,用于将原有二维结构扁平化。可以将结果转化为 Stream<String> 这种形式,方便之后的其他操作; - flatMapToInt:用法参考 flatMap,将元素扁平为 int 类型;
- flatMapToLong:用法参考 flatMap,将元素扁平为 Long 类型;
- flatMapToDouble:用法参考 flatMap,将元素扁平为 Double 类型。
@Test
public void testFlatMap() {
List<List<Integer>> list = new ArrayList<>();
list.add(IntStream.range(0, 5).boxed().collect(Collectors.toList()));
list.add(IntStream.range(5, 7).boxed().collect(Collectors.toList()));
list.forEach(System.out::print); // [0, 1, 2, 3, 4][5, 6]
System.out.println("\n==================");
Stream<List<Integer>> stream = list.stream();
stream.flatMap(Collection::stream).forEach(System.out::print); // 0123456
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 1.15 collection
把结果变成 List、Map 这样的常用数据结构
@Test
public void testCollection() {
List<Integer> list = IntStream.range(0, 5).boxed().collect(Collectors.toList());
System.out.println(list.size()); // 5
Set<Integer> set = list.stream().collect(Collectors.toSet());
System.out.println(set.size()); // 5
}
1
2
3
4
5
6
7
2
3
4
5
6
7
# 1.16 toArray
返回数组,有两个重载,一个空参数,返回的是 Object[]
;另一个是入参 IntFunction<R>
类型参数
@Test
public void testToArray() {
Integer[] i = IntStream.range(0, 5).boxed().toArray(Integer[]::new);
System.out.println(i.length); // 5
}
1
2
3
4
5
2
3
4
5
# 二:函数式接口
# 2.1 Supplier
你要作为一个供应者,自己生产数据
Supplier<T> 源码
package java.util.function;
/**
* 代表结果的supplier(供应者)
*
* 不要求每次调用 supplier 时都返回新的或不同的结果
* 这是一个函数式接口,其函数式方法是get()
*
* @param 该 supplier 提供的结果类型
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* 得到结果
*
* @return 一个结果
*/
T get();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
测试
public class SupplierTest {
public static void main(String[] args) {
List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());
int length = getLength(list::size);
System.out.println("list长度:" + length); // list长度:10
Integer[] array = new Integer[]{1, 2, 3, 4};
length = getLength(() -> array.length);
System.out.println("数组长度:" + length); // 数组长度:4
}
/**
* 获取 数组/集合 长度
*
* @return 长度
*/
private static int getLength(Supplier<Integer> supplier) {
return supplier.get();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2.2 Consumer
你要作为一个消费者,利用已经准备数据
Consumer<T> 源码
package java.util.function;
/**
* 表示接受单个输入参数且不返回结果的操作。与大多数其他功能接口不同,Consumer(消费者) 预计将通过 side-effects(副作用) 进行操作
*
* 这是一个函数式接口,其函数式方法是accept(Object)
*
* @param <T> 操作的输入类型
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* 对给定参数执行此操作
*
* @param t 输入参数
*/
void accept(T t);
/**
* 返回一个组合的 Consumer(消费者),它按顺序执行此操作,然后是 参数after 操作。
* 如果执行任一操作引发异常,则将其转发给组合操作的调用者。
* 如果执行此操作引发异常,则不会执行 参数after 操作。
*
* @param after 此操作后要执行的操作
* @return 一个组合的Consumer(消费者),它按顺序执行此操作,然后执行 参数after 操作
* @throws NullPointerException 如果 参数after 为 null
*/
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
测试
public class ConsumerTest {
public static void main(String[] args) {
print(name -> System.out.printf("姓名:%s ", name.split(",")[0]),
sex -> System.out.printf("性别:%s \n", sex.split(",")[1]),
new String[]{"张三,男"});
}
/**
* 打印信息
*
* @param name 名字
* @param sex 性别
* @param array 内容
*/
private static void print(Consumer<String> name, Consumer<String> sex, String[] array) {
for (String s : array) {
name.andThen(sex).accept(s); // 姓名:张三 性别:男
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 2.3 Predicate
输入一个或者两个不同或者相同的值总是输出boolean
Predicate<T> 源码
package java.util.function;
/**
* 表示一个参数的predicate(谓词) (布尔值函数)
* 这是一个函数式接口,其函数式方法是test(Object)
*
* @param <T> predicate(谓词)的输入类型
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* 根据给定参数评估此predicate(谓词).
*
* @param t 输入参数
* @return 如果输入参数与谓词匹配,则为 true,否则为 false
*/
boolean test(T t);
/**
* 返回一个组合谓词,该谓词表示此谓词与另一个谓词的短路逻辑与。在评估组合谓词时,如果该谓词为假,则不评估另一个谓词。
* 在评估任一谓词期间抛出的任何异常都将转发给调用者; 如果此谓词的评估引发异常,则不会评估另一个谓词。
*
* @param other 将与该谓词进行逻辑与运算的谓词
* @return 一个组合谓词,表示该谓词与另一个谓词的短路逻辑与
* @throws NullPointerException 如果参数(other)为null
*/
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
/**
* 返回表示此谓词的逻辑否定的谓词。
*
* @return 表示此谓词的逻辑否定的谓词
*/
default Predicate<T> negate() {
return (t) -> !test(t);
}
/**
* 返回一个组合谓词,该谓词表示此谓词与另一个谓词的短路逻辑或。在评估组合谓词时,如果该谓词为真,则不评估另一个谓词。
* 在评估任一谓词期间抛出的任何异常都将转发给调用者;如果此谓词的评估引发异常,则不会评估另一个谓词。
*
* @param other 将与此谓词进行逻辑或的谓词
* @return 一个组合谓词,表示该谓词与另一个谓词的短路逻辑或
* @throws NullPointerException 如果参数(other)为null
*/
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
/**
* 返回根据 Objects.equals(Object, Object) 测试两个参数是否相等的谓词。
*
* @param <T> 谓词的参数类型
* @param targetRef 用于比较相等性的对象引用,可能为 null
* @return 根据 Objects.equals(Object, Object) 测试两个参数是否相等的谓词
*/
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
测试
public class PredicateTest {
public static void main(String[] args) {
validate((s) -> s.split(",")[0].length() == 3,
(s) -> s.split(",")[1].equals("女"),
new String[]{"迪丽热巴,女", "古力娜扎,女", "马尔扎哈,男", "赵丽颖,女"});
}
/**
* 1. 性别为女
* 2. 姓名为3个字
*
* @param name 判断姓名为3个字的谓词
* @param sex 判断性别的谓词
* @param data 基础数据
*/
private static void validate(Predicate<String> name, Predicate<String> sex, String[] data) {
for (String s : data) {
if (name.and(sex).test(s)) {
System.out.println("满足条件的数据:" + s); // 满足条件的数据:赵丽颖,女
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 2.4 Function
输入一个或者两个不同或者相同的值转为另一个值
Function<T, R> 源码
package java.util.function;
import java.util.Objects;
/**
* 表示接受一个参数并产生结果的函数。
* 这是一个函数式接口,其函数式方法是test(Object)
*
* @param <T> 函数的输入类型
* @param <R> 函数结果的类型
*
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* 将此 function(函数) 应用于给定的参数。
*
* @param t function 参数
* @return function 结果
*/
R apply(T t);
/**
* 返回一个组合函数,该函数首先将 before函数 应用于其输入,然后将此函数应用于结果。如果任一函数的求值引发异常,则将其转发给组合函数的调用者。
*
* @param <V> before函数和组合函数的输入类型
* @param before 参数before
* @return 一个组合函数,它首先应用 before 函数,然后应用这个函数
* @throws NullPointerException 如果参数before为null
*
* @see #andThen(Function)
*/
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
/**
* 返回一个组合函数,该函数首先将此函数应用于其输入,然后将 after 函数应用于结果。如果任一函数的求值引发异常,则将其转发给组合函数的调用者。
*
* @param <V> after 函数和组合函数的输出类型
* @param after 应用此功能后要应用的功能
* @return 一个组合函数,首先应用此函数,然后应用 after 函数
* @throws NullPointerException 如果参数after为null
*
* @see #compose(Function)
*/
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
/**
* 返回一个始终返回其输入参数的函数。
*
* @param <T> 函数的输入和输出对象的类型
* @return 始终返回其输入参数的函数
*/
static <T> Function<T, T> identity() {
return t -> t;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
测试
public class FunctionTest {
public static void main(String[] args) {
compose(s -> s.split(",")[1],
Integer::valueOf,
s -> s * 100,
"柯南,10");
}
/**
* 1. 得到数字部分
* 2. 数字部分转 Integer
* 3. 加 100
*
* @param f1 取数字部分
* @param f2 转换 Integer
* @param f3 加 100
* @param data 待处理的数据
*/
private static void compose(Function<String, String> f1, Function<String, Integer> f2, Function<Integer, Integer> f3, String data) {
Integer apply = f1.andThen(f2).andThen(f3).apply(data);
System.out.println("最后结果:" + apply);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 2.5 Operator
UnaryOperator 输入一个值转换为相同值输出
BinaryOperator 输入两个相同类型的值转为相同类型的值输出
https://lw900925.github.io/java/java8-lambda-expression.html https://lw900925.github.io/java/java8-stream-api.html https://lw900925.github.io/java/java8-optional.html