JAVA8特性 & Stream流 & 函数式编程Lambda表达式 & 双冒号(::)的用法和原理

一、自己的理解 函数式编程

从学习JAVA到现在一直都是面向对象的编程思想,一直以来的编程思想都是将相同类型的对象抽象出来形成类,但是函数式编程的思想和这种思想刚好相反,它是将方法抽象出来形成统一接口,输入的方式得到了本质的变化,是不是理解起来还是有点抽象,那么请看下图:

传统编程思想

方法不变,输入不同值得到不同结果。

函数式编程思想

值不变,输入不同方法得到不同结果。

二、Lambda表达式

Lambda表达式是Java 8引入的一种语法特性,它可以简化函数式编程的代码书写。

格式

java 复制代码
@Test
public void test() throws Exception {
    Runnable runnable = () -> { System.out.println("thread success"); };
}
  • ():表示该Lambda表达式没有参数。在这个例子中,Lambda表达式不接受任何参数。
  • ->:箭头符号,将参数列表和Lambda表达式的主体分隔开。
  • {}:花括号内的代码块,表示Lambda表达式的具体逻辑。在这个例子中,Lambda表达式的主体是打印"thread success"。

条件

scss 复制代码
Lambda表达式只能用于函数式接口(Functional Interface)或者SAM(Single Abstract Method 单个抽象方法)类型。函数式接口是指只有一个抽象方法的接口,可以通过`@FunctionalInterface`注解进行标识。Runnable就是函数式接口。
java 复制代码
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

应用

1、作为函数式接口的实例

Lambda表达式可以作为函数式接口的实例,函数式接口是只有一个抽象方法的接口。通过Lambda表达式,可以直接实现函数式接口的抽象方法,而不需要显式地编写实现类。

java 复制代码
Runnable runnable = () -> {
    // 执行具体逻辑
};

2、作为方法的参数

Lambda表达式可以作为方法的参数进行传递。这样可以在调用方法时,直接在参数位置上使用Lambda表达式来定义方法的具体逻辑。

java 复制代码
public void doSomething(Function<String, Integer> function) {
    // 执行具体逻辑
}

doSomething((String s) -> s.length());

3、作为方法的返回值

Lambda表达式可以作为方法的返回值进行返回。这样可以在方法内部使用Lambda表达式来定义方法的具体逻辑,并将其返回给调用者。

java 复制代码
public Function<String, Integer> createFunction() {
    return (String s) -> s.length();
}

三、Lambda表达书中的双冒号

语法

java 复制代码
list.forEach(System.out::println);

应用

静态方法引用

可以引用一个类的静态方法。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.forEach(System.out::println);

实例方法引用

可以引用一个特定对象的实例方法。

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(String::toUpperCase);

构造函数引用

可以引用一个类的构造函数。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Person> persons = numbers.stream().map(Person::new).collect(Collectors.toList());

四、Stream流的用法

1 filter(Predicate<T> predicate) 根据给定的条件过滤出符合条件的元素并返回一个新的Stream。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
                                   .filter(num -> num % 2 == 0)
                                   .collect(Collectors.toList());
// 过滤出偶数 [2, 4]
  • Predicate<T>函数式接口,可以理解为是一个返回值为Boolean类型的方法,T为输入类型。

2 map(Function<T, R> mapper) 将每个元素通过给定的映射函数转换为另一种类型,并返回一个新的Stream。

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<Integer> nameLengths = names.stream()
                                 .map(String::length)
                                 .collect(Collectors.toList());
// 转换为名字的长度 [5, 3, 7]
  • Function<T, R>函数式接口,可以理解为是一个转化方法,T为输入,R为输出。

3 flatMap(Function<T, Stream<R>> mapper) 将每个元素通过给定的映射函数转换为Stream并将多个Stream合并为一个Stream。

java 复制代码
List<List<Integer>> numbers = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5, 6));
List<Integer> flattenedNumbers = numbers.stream()
                                        .flatMap(List::stream)
                                        .collect(Collectors.toList());
// 合并成一个列表 [1, 2, 3, 4, 5, 6]
  • Function<T, Stream<R>>函数式接口,可以理解为是一个转化Stream方法,T为输入,Stream<R>为输出。

4 distinct() 去除重复元素,返回一个去重后的新Stream。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 5, 5);
List<Integer> distinctNumbers = numbers.stream()
                                       .distinct()
                                       .collect(Collectors.toList());
// 去除重复的数字 [1, 2, 3, 4, 5]

5 sorted() 对Stream中的元素进行排序,默认按照自然顺序进行排序。

java 复制代码
List<Integer> numbers = Arrays.asList(5, 2, 4, 1, 3);
List<Integer> sortedNumbers = numbers.stream()
                                     .sorted()
                                     .collect(Collectors.toList());
// 排序后的数字列表 [1, 2, 3, 4, 5]

6 sorted(Comparator<T> comparator) 根据给定的Comparator对Stream中的元素进行排序。

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
List<String> sortedNames = names.stream()
                               .sorted(Comparator.reverseOrder())
                               .collect(Collectors.toList());
// 按字母降序排序 ["Charlie", "Bob", "Alice"]

7 limit(long maxSize) 截取Stream中的前maxSize个元素。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> limitedNumbers = numbers.stream()
                                      .limit(3)
                                      .collect(Collectors.toList());
// 截取前3个数字 [1, 2, 3]

8 skip(long n) 跳过Stream中的前n个元素。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> skippedNumbers = numbers.stream()
                                      .skip(2)
                                      .collect(Collectors.toList());
// 跳过前2个数字 [3, 4, 5]

9 forEach(Consumer<T> action) 对Stream中的每个元素执行给定的操作。

java 复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.stream()
     .forEach(System.out::println);
// 打印每个名字

10 collect(Collector<T, A, R> collector) 将Stream中的元素收集到一个集合或数据结构中,并返回结果。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> collectedNumbers = numbers.stream()
                                        .collect(Collectors.toList());
// 收集到列表中 [1, 2, 3, 4, 5]

扩展(可以不看):

Collector<T, A, R> 是 Java 中用于将流中的元素进行收集的接口。它定义了三个泛型参数:

T:表示流中元素的类型。

A:表示用于累积部分结果的对象的类型,也可以看作是中间结果的类型。

R:表示收集操作的最终结果的类型。

Collector<T, A, R> 接口中定义了以下五个方法:
Supplier<A> supplier():用于创建一个初始的结果容器对象。
BiConsumer<A, T> accumulator():用于将流中的元素累积到结果容器中。
BinaryOperator<A> combiner():用于将两个结果容器合并为一个。
Function<A, R> finisher():用于将结果容器转换为最终结果。
Set<Characteristics> characteristics():用于获取收集器的特性,返回一个包含特性的不可变 Set 集合。

重点:
Collector<T, A, R> collector JAVA8中有专门定义收集器的工具类Collectors,以下列出常用的方法:

  • toList() 将流中的元素收集到一个 List 中。
java 复制代码
List<String> names = Stream.of("John", "Alice", "Bob")
                        .collect(Collectors.toList());
  • toSet() 将流中的元素收集到一个 Set 中。
java 复制代码
Set<Integer> numbers = Stream.of(1, 2, 3, 4, 5)
                        .collect(Collectors.toSet());
  • toMap() 将流中的元素根据指定的键和值的提取函数收集到一个 Map 中。
java 复制代码
Map<String, Integer> ageMap = Stream.of("John", "Alice", "Bob")
                                .collect(Collectors.toMap(Function.identity(), String::length));
  • counting() 对流中的元素进行计数。
java 复制代码
long count = Stream.of("John", "Alice", "Bob")
            .collect(Collectors.counting());
  • summingInt() 对流中的元素进行求和,返回一个整数。
java 复制代码
int sum = Stream.of(1, 2, 3, 4, 5)
            .collect(Collectors.summingInt(Integer::intValue));
  • averagingInt() 对流中的元素进行求平均值,返回一个 double 类型。
java 复制代码
double average = Stream.of(1, 2, 3, 4, 5)
                .collect(Collectors.averagingInt(Integer::intValue));
  • joining() 将流中的元素拼接成一个字符串。
java 复制代码
String result = Stream.of("Hello", "World")
                .collect(Collectors.joining(", "));
  • maxBy() 根据指定的比较器选择流中的最大元素。
java 复制代码
Optional<String> maxName = Stream.of("John", "Alice", "Bob")
                        .collect(Collectors.maxBy(Comparator.naturalOrder()));
  • minBy() 根据指定的比较器选择流中的最小元素。
java 复制代码
Optional<String> minName = Stream.of("John", "Alice", "Bob")
                        .collect(Collectors.minBy(Comparator.naturalOrder()));
  • groupingBy() 根据指定的分类函数对流中的元素进行分组。
java 复制代码
Map<Character, List<String>> groupedNames = Stream.of("John", "Alice", "Bob")
                                            .collect(Collectors.groupingBy(s -> s.charAt(0)));
  • partitioningBy() 根据指定的条件对流中的元素进行分区。
java 复制代码
Map<Boolean, List<String>> partitionedNames = Stream.of("John", "Alice", "Bob")
                                                .collect(Collectors.partitioningBy(s -> s.length() > 3));
  • mapping() 对流中的元素进行映射,并将结果收集到一个集合中。
java 复制代码
List<Integer> lengths = Stream.of("John", "Alice", "Bob")
                        .collect(Collectors.mapping(String::length, Collectors.toList()));
  • filtering() 根据指定的条件过滤流中的元素,并将结果收集到一个集合中。
java 复制代码
List<String> filteredNames = Stream.of("John", "Alice", "Bob")
                            .collect(Collectors.filtering(s -> s.length() > 3, Collectors.toList()));
  • reducing() 根据指定的规约操作对流中的元素进行规约。
java 复制代码
Optional<Integer> sum = Stream.of(1, 2, 3, 4, 5)
                        .collect(Collectors.reducing(Integer::sum));
  • collectingAndThen() 对流中的元素进行收集,并执行一个最终的转换操作。
java 复制代码
String longestName = Stream.of("John", "Alice", "Bob")
                        .collect(Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(String::length)), Optional::get));
  • toCollection() 将流中的元素收集到指定的集合类型中。
java 复制代码
LinkedList<String> names = Stream.of("John", "Alice", "Bob")
                            .collect(Collectors.toCollection(LinkedList::new));
  • summarizingInt() 对流中的元素进行统计,返回一个统计结果对象。
java 复制代码
IntSummaryStatistics statistics = Stream.of(1, 2, 3, 4, 5)
                                    .collect(Collectors.summarizingInt(Integer::intValue));
  • groupingByConcurrent() 并发地根据指定的分类函数对流中的元素进行分组。
java 复制代码
ConcurrentMap<Character, List<String>> concurrentGroupedNames = Stream.of("John", "Alice", "Bob")
                                                                .collect(Collectors.groupingByConcurrent(s -> s.charAt(0)));
  • summingDouble() 对流中的元素进行求和,返回一个 double 类型。
java 复制代码
double sum = Stream.of(1.0, 2.0, 3.0, 4.0, 5.0)
            .collect(Collectors.summingDouble(Double::doubleValue));
  • toConcurrentMap() 将流中的元素根据指定的键和值的提取函数收集到一个并发的 Map 中。
java 复制代码
ConcurrentMap<String, Integer> concurrentAgeMap = Stream.of("John", "Alice", "Bob")
                                                .collect(Collectors.toConcurrentMap(Function.identity(), String::length));

11 toArray() 将Stream中的元素转换为数组。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Integer[] numberArray = numbers.stream()
                               .toArray(Integer[]::new);
// 转换为数组 [1, 2, 3, 4, 5]

12 anyMatch(Predicate<T> predicate) 判断Stream中是否存在满足给定条件的元素。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean hasEvenNumber = numbers.stream()
                              .anyMatch(num -> num % 2 == 0);
// 是否有偶数,结果为true

13 allMatch(Predicate<T> predicate) 判断Stream中的所有元素是否都满足给定条件。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean allEvenNumbers = numbers.stream()
                               .allMatch(num -> num % 2 == 0);
// 是否都是偶数,结果为false

14 noneMatch(Predicate<T> predicate) 判断Stream中是否不存在满足给定条件的元素。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
boolean noEvenNumber = numbers.stream()
                             .noneMatch(num -> num % 2 == 0);
// 是否没有偶数,结果为false

15 findFirst() 返回Stream中的第一个元素。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> firstNumber = numbers.stream()
                                       .findFirst();
// 第一个数字,结果为Optional[1]

16 findAny() 返回Stream中的任意一个元素。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> anyNumber = numbers.stream().findAny();
// 任意一个数字,结果为Optional[1]或其他数字

17 count() 返回Stream中的元素个数。

java 复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
long count = numbers.stream().count();
// 元素个数,结果为5
相关推荐
西海天际蔚蓝5 分钟前
递归查询全量分页数据问题
java
SomeB1oody10 分钟前
【Rust自学】7.4. use关键字 Pt.2 :重导入与换国内镜像源教程
开发语言·后端·rust
新知图书15 分钟前
Rust编程与项目实战-箱
开发语言·后端·rust
俎树振18 分钟前
深入理解与优化Java二维数组:从定义到性能提升的全面指南
java·算法
SomeB1oody19 分钟前
【Rust自学】7.3. use关键字 Pt.1:use的使用与as关键字
开发语言·后端·rust
DARLING Zero two♡26 分钟前
【优选算法】Sliding-Chakra:滑动窗口的算法流(上)
java·开发语言·数据结构·c++·算法
minstbe29 分钟前
WEB开发 - Flask 入门:Jinja2 模板语法进阶 Python
后端·python·flask
love静思冥想31 分钟前
Apache Commons ThreadUtils 的使用与优化
java·线程池优化
君败红颜32 分钟前
Apache Commons Pool2—Java对象池的利器
java·开发语言·apache
意疏41 分钟前
JDK动态代理、Cglib动态代理及Spring AOP
java·开发语言·spring