Lambda

5/21/2022 Java

摘要

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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 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

# 1.9 reduce

可以在 peek 方法里面做任何没有返回值的事情,比如打印日志

public void testPeek() {
	IntStream.range(0, 10).boxed().peek(System.out::print).collect(Collectors.toList()); // 0123456789
}
1
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

# 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

# 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

# 1.13 limit

跳过前 n 条数据

@Test
public void testSkip() {
	IntStream.range(0, 5).skip(3).forEach(System.out::print);   // 34
}
1
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

# 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

# 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.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

测试

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.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

测试

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 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

测试

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.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

测试

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.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

# 三:参考文献

最后更新: 10/3/2022, 9:53:11 AM