JAVA8 Steam编程 lambda

总结

filter

filter 是 Stream 接口提供的一个中间操作方法。filter 方法的主要作用是根据指定的条件对 Stream 中的元素进行筛选,只保留满足条件的元素,最终返回一个包含筛选后元素的新 Stream。
从功能上,.filter(f(e)) f(e) 是条件判断语句

map

map 是 Stream 接口提供的一个中间操作方法。map 方法用于将流中的每个元素通过一个指定的函数进行转换,然后返回一个包含转换后元素的新流。
从提供的功能可以联想到, map的使用 .map(e -> f(e)) f(e) 是转化函数

flatMap

flatMap 是一个强大的中间操作方法,它主要用于将一个流中的每个元素转换为另一个流,然后将这些流合并成一个单一的流。下面详细介绍 flatMap 的用法。
flatMap 常用于处理嵌套结构的数据,例如一个集合中的每个元素又是一个集合,你想把这些嵌套的集合合并成一个单一的集合。
用法上: flatMap(e -> Arrays.stream(转化为数组) 。 概括的讲, 先转化为列表,再stream

详细用法

filter用法

Stream 接口的 filter 方法定义如下:
T 是 Stream 中元素的类型,predicate 是一个 Predicate 函数式接口,它接收一个类型为 T 的参数,并返回一个布尔值,表示该元素是否满足筛选条件。
Stream<T> filter(Predicate<? super T> predicate);

例子

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FilterEvenNumbersExample {
    public static void main(String[] args) {
        // 创建一个整数列表
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // 使用 filter 方法筛选出偶数
        List<Integer> evenNumbers = numbers.stream()
               .filter(n -> n % 2 == 0)
               .collect(Collectors.toList());

        // 输出结果
        System.out.println(evenNumbers); 
    }
}

map

其中,T 是原始流中元素的类型,R 是转换后元素的类型。mapper 是一个 Function 函数式接口,它接收一个类型为 T 的参数,并返回一个类型为 R 的结果。

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamMapExample {
    public static void main(String[] args) {
        // 创建一个整数列表
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 使用 map 方法对每个元素进行平方操作
        List<Integer> squaredNumbers = numbers.stream()
               .map(n -> n * n)
               .collect(Collectors.toList());

        // 输出结果
        System.out.println(squaredNumbers); 
    }
}

在上述代码中,map(n -> n * n) 表示对 numbers 流中的每个元素 n 进行平方操作,然后使用 collect(Collectors.toList()) 将转换后的元素收集到一个新的列表中。

2. 将字符串列表中的每个字符串转换为大写

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StringToUpperCaseExample {
    public static void main(String[] args) {
        // 创建一个字符串列表
        List<String> words = Arrays.asList("apple", "banana", "cherry");

        // 使用 map 方法将每个字符串转换为大写
        List<String> upperCaseWords = words.stream()
               .map(String::toUpperCase)
               .collect(Collectors.toList());

        // 输出结果
        System.out.println(upperCaseWords); 
    }
}

这里,map(String::toUpperCase) 使用了方法引用,将 words 流中的每个字符串转换为大写形式。

方法引用的概念

方法引用是一种更简洁的 lambda 表达式语法,它允许直接引用已有的方法或构造函数,而不需要显式地编写 lambda 表达式的参数列表和方法体。方法引用通过 :: 符号来表示,它可以使代码更加简洁易读。
String::toUpperCase 是 e -> e.toUpperCase() 的一种简洁表示方式,它们在功能上是等价的。在实际开发中,可以根据代码的简洁性和可读性需求选择合适的方式。

flatMap

在 Java 8 引入的 Stream API 中,flatMap 是一个强大的中间操作方法,它主要用于将一个流中的每个元素转换为另一个流,然后将这些流合并成一个单一的流。下面详细介绍 flatMap 的用法。

基本概念和语法

Stream 接口中的 flatMap 方法定义如下:

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
  • T 是原始流中元素的类型。
  • R 是最终流中元素的类型。
  • mapper 是一个 Function 函数式接口,它接收一个类型为 T 的元素,并返回一个类型为 Stream<? extends R> 的流。

使用场景

flatMap 常用于处理嵌套结构的数据,例如一个集合中的每个元素又是一个集合,你想把这些嵌套的集合合并成一个单一的集合。

示例代码

1. 处理字符串列表中的单词

假设你有一个字符串列表,每个字符串包含多个用空格分隔的单词,你想把这些单词提取出来形成一个单一的单词列表。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FlatMapStringExample {
    public static void main(String[] args) {
        List<String> sentences = Arrays.asList("Hello world", "Java programming", "Stream API");

        List<String> words = sentences.stream()
               .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
               .collect(Collectors.toList());

        System.out.println(words);
    }
}

代码解释

  • sentences.stream():将 sentences 列表转换为一个流。
  • flatMap(sentence -> Arrays.stream(sentence.split(" "))):对于流中的每个句子,使用 split(" ") 方法将其拆分为单词数组,然后使用 Arrays.stream() 将数组转换为流。flatMap 会将这些流合并成一个单一的流。
  • collect(Collectors.toList()):将合并后的流中的元素收集到一个列表中。

2. 处理嵌套的集合

假设有一个包含多个列表的列表,你想把这些嵌套的列表合并成一个单一的列表。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FlatMapListExample {
    public static void main(String[] args) {
        List<List<Integer>> nestedList = Arrays.asList(
                Arrays.asList(1, 2, 3),
                Arrays.asList(4, 5),
                Arrays.asList(6, 7, 8)
        );

        List<Integer> flattenedList = nestedList.stream()
               .flatMap(List::stream)
               .collect(Collectors.toList());

        System.out.println(flattenedList);
    }
}

代码解释

  • nestedList.stream():将 nestedList 转换为一个流,流中的每个元素是一个 List<Integer>
  • flatMap(List::stream):对于流中的每个列表,使用 List::stream 方法将其转换为一个流。flatMap 会将这些流合并成一个单一的流。
  • collect(Collectors.toList()):将合并后的流中的元素收集到一个列表中。

3. 处理对象中的集合属性

假设你有一个 Person 类,每个 Person 对象有一个 List<String> 类型的 hobbies 属性,你想把所有 Person 对象的 hobbies 合并成一个单一的列表。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Person {
    private String name;
    private List<String> hobbies;

    public Person(String name, List<String> hobbies) {
        this.name = name;
        this.hobbies = hobbies;
    }

    public List<String> getHobbies() {
        return hobbies;
    }
}

public class FlatMapObjectExample {
    public static void main(String[] args) {
        List<Person> persons = Arrays.asList(
                new Person("Alice", Arrays.asList("Reading", "Swimming")),
                new Person("Bob", Arrays.asList("Running", "Painting")),
                new Person("Charlie", Arrays.asList("Gaming", "Hiking"))
        );

        List<String> allHobbies = persons.stream()
               .flatMap(person -> person.getHobbies().stream())
               .collect(Collectors.toList());

        System.out.println(allHobbies);
    }
}

代码解释

  • persons.stream():将 persons 列表转换为一个流。
  • flatMap(person -> person.getHobbies().stream()):对于流中的每个 Person 对象,获取其 hobbies 列表并将其转换为一个流。flatMap 会将这些流合并成一个单一的流。
  • collect(Collectors.toList()):将合并后的流中的元素收集到一个列表中。

Map computeIfAbsent

示例代码

Map<Long, List<DynamicObject>> orgToBills = new HashMap<>();
for (DynamicObject bill : bills) {
    DynamicObject org = (DynamicObject) bill.get(quoteOrgType);
    
    orgToBills.computeIfAbsent((Long)org.getPkValue(), key -> new ArrayList<>()).add(bill);
}

如果当前 key 不存在于 map 中,则使用给定的函数计算出一个 value,并放入 map 中,然后返回这个 value;
如果 key 已经存在,则直接返回已有的 value。

等价JDK7的写法

Long orgPk = (Long) org.getPkValue();
if (!orgToBills.containsKey(orgPk)) {
    orgToBills.put(orgPk, new ArrayList<DynamicObject>());
}
orgToBills.get(orgPk).add(bill);

computeIfAbsent其他例子

Map<String, Integer> map = new HashMap<>();
        map.put("apple", 1);
        Integer result = map.computeIfAbsent("apple", k -> 2); // 返回1,因为"apple"已存在
        result = map.computeIfAbsent("banana", k -> 3); // 返回3,因为"banana"不存在,并添加到Map中

Collectors.toMap

Collectors.toMap 是 Java Stream API 中的一个强大工具,用于将流中的元素收集到一个 Map 中。下面详细介绍其用法、参数和常见场景。

基本用法

Collectors.toMap 有三个重载方法,最常用的是接收三个参数的版本:

Map<K, V> map = stream.collect(Collectors.toMap(
    keyMapper,      // 从元素中提取键的函数
    valueMapper,    // 从元素中提取值的函数
    mergeFunction   // 处理键冲突的合并函数
));

参数详解

1. keyMapper

  • 作用:从流中的元素中提取键。
  • 示例
    List<Person> people = Arrays.asList(
        new Person(1, "Alice"),
        new Person(2, "Bob")
    );
    
    // 将 Person 的 id 作为键,name 作为值
    Map<Integer, String> idToName = people.stream()
        .collect(Collectors.toMap(
            person -> person.getId(),  // 键映射:Person::getId
            person -> person.getName() // 值映射:Person::getName
        ));
    

2. valueMapper

  • 作用:从流中的元素中提取值。
  • 示例
    // 将 Person 对象本身作为值
    Map<Integer, Person> idToPerson = people.stream()
        .collect(Collectors.toMap(
            Person::getId,    // 键映射
            person -> person  // 值映射:Function.identity()
        ));
    

3. mergeFunction

  • 作用:当出现重复键时,指定如何合并值。
  • 示例
    List<Person> peopleWithDuplicates = Arrays.asList(
        new Person(1, "Alice"),
        new Person(1, "Bob")  // 重复的 id=1
    );
    
    // 保留最后出现的值(覆盖旧值)
    Map<Integer, String> idToName = peopleWithDuplicates.stream()
        .collect(Collectors.toMap(
            Person::getId,
            Person::getName,
            (existing, replacement) -> replacement  // 合并函数
        ));
    

常见场景

1. 处理键冲突

  • 覆盖旧值(oldValue, newValue) -> newValue
  • 合并值(例如累加):(oldValue, newValue) -> oldValue + newValue
  • 抛出异常(oldValue, newValue) -> { throw new IllegalStateException(); }

2. 空值处理

  • 过滤空值
    Map<Integer, String> nonNullMap = people.stream()
        .filter(person -> person.getName() != null)  // 过滤 null 值
        .collect(Collectors.toMap(
            Person::getId,
            Person::getName
        ));
    
  • 默认值
    person -> person.getName() != null ? person.getName() : "Unknown"
    

3. 不可变 Map

import static java.util.stream.Collectors.*;

Map<Integer, String> immutableMap = people.stream()
    .collect(Collectors.toUnmodifiableMap(
        Person::getId,
        Person::getName
    ));

完整示例

import java.util.*;
import java.util.stream.Collectors;

class Person {
    private final int id;
    private final String name;

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() { return id; }
    public String getName() { return name; }

    @Override
    public String toString() {
        return "Person{id=" + id + ", name='" + name + "'}";
    }
}

public class Main {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person(1, "Alice"),
            new Person(2, "Bob"),
            new Person(2, "Charlie")  // 重复的 id=2
        );

        // 使用合并函数处理重复键
        Map<Integer, String> idToName = people.stream()
            .collect(Collectors.toMap(
                Person::getId,
                Person::getName,
                (existing, replacement) -> existing + " & " + replacement  // 合并名字
            ));

        System.out.println(idToName);  // 输出: {1=Alice, 2=Bob & Charlie}
    }
}

注意事项

  1. 键不可重复:若流中存在重复键且未提供合并函数,会抛出 IllegalStateException
  2. 空值风险:键或值为 null 可能导致 NullPointerException,需提前过滤或处理。
  3. 顺序问题:若需保持流中元素的顺序,可使用 Collectors.toMap 的重载方法并传入 LinkedHashMap::new 作为第四个参数。

掌握 Collectors.toMap 的用法后,你可以轻松实现集合到映射的转换,处理复杂的数据聚合需求。

参考资料

posted @ 2025-02-21 09:41  向着朝阳  阅读(55)  评论(0)    收藏  举报