集合处理常用Stream流


stream流经常使用,但是遇到一些流操作时,会一下想不到用哪种,这里总结一下,方便自己或者读者查找

1、Stream API介绍

Stream API是Java 8引入的一项重要特性,它提供了一种新的处理集合数据的方式。Stream可以看作是一种高级的迭代器,它允许以声明式的方式对集合进行各种操作,如过滤、映射、排序、归约等。它可以简化集合处理的代码,并且在处理大数据集时具有性能优势。
个人理解:stream流就像一条源源不断的流水线或者管道,原材料从一端进入生产线,经过一系列的加工和处理,最终成品从另一端输出。在这个过程中,每个工人(方法)负责自己的任务,并将结果传递给下一个工人(方法),形成一个连续的操作链。直到遇到终止方法,才会生成最后的结果。

2、List集合常用Stream方法

1、stream(): 返回一个顺序流(Stream)。parallelStream(): 返回一个并行流(Stream)。

并行处理:Stream API提供了并行处理的能力,可以利用多线程来加速大数据集的处理。通过调用parallelStream()方法,可以将流转换为并行流,使得操作可以并行地执行。

List<String> list = Arrays.asList("apple", "banana", "cat");
Stream<String> stream = list.stream();
Stream<String> parallelStream = list.parallelStream();

2、filter(Predicate predicate): 过滤元素,保留符合条件的元素。传入是一个Predicate函数式接口,返回true:保留,false: 丢弃

List<String> list = Arrays.asList("apple", "banana", "cat");
Stream<String> filteredStream = list.stream().filter(s -> s.startsWith("a"));

3、distinct(): 去除重复的元素。

List<String> list = Arrays.asList("apple", "banana", "apple");
Stream<String> distinctStream = list.stream().distinct();

4、sorted(): 对元素进行排序,默认使用自然顺序。

List<Integer> list = Arrays.asList(3, 1, 2);
Stream<Integer> sortedStream = list.stream().sorted();

4.1 也可以自定义比较器,也就是参数中需要填写一个比较器 “sorted(Comparator<? super T> comparator)” 例如:

//字符串忽略大小写并排序
List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");
List<String> sortedNames = names.stream().sorted((a, b) -> a.compareToIgnoreCase(b)).collect(Collectors.toList());

//对引用类型排序,通过其中的一个属性排序
class Person {
    
    
    private String name;
    private int age;

    // 省略构造函数和其他方法

    // Getter和Setter方法

    // ...
}
List<Person> persons = Arrays.asList(
        new Person("Alice", 25),
        new Person("Bob", 30),
        new Person("Charlie", 20)
);

List<Person> sortedPersons = persons.stream()
        .sorted(Comparator.comparingInt(Person::getAge))
        .collect(Collectors.toList());

5、limit(long maxSize): 限制流中元素的数量。

List<String> list = Arrays.asList("apple", "banana", "cat");
Stream<String> limitedStream = list.stream().limit(2);

6、forEach(Consumer action): 对每个元素执行给定的操作。就是遍历

List<String> list = Arrays.asList("apple", "banana", "cat");
list.stream().forEach(System.out::println);

7、map(Function<T, R> mapper): 将元素进行映射,得到一个新的流。这个不是转为一个map,该方法接受一个Function<T, R>函数接口作为参数,该函数接受一个输入类型为T的元素,并返回一个输出类型为R的结果。

List<String> list = Arrays.asList("apple", "banana", "cat");
Stream<Integer> lengthStream = list.stream().map(String::length);

7.1 map()方法允许我们在流中对每个元素进行自定义的转换操作,并生成一个新的流。通过传递适当的Function函数接口实现,我们可以实现各种转换操作,如类型转换、属性提取、计算转换等。例如:

class Person {
    
    
    private String name;
    // ...
}

List<Person> persons = Arrays.asList(
        new Person("Alice"),
        new Person("Bob"),
        new Person("Charlie")
);
//提取name,生成一个新的List<String>
List<String> names = persons.stream()
        .map(Person::getName)
        .collect(Collectors.toList());
//将一个person转换为一个student
 List<Student> students = persons.stream()
                .map(person -> {
    
    
                    String name = person.getName();
                    int age = person.getAge();
                    String school = getSchoolBasedOnAge(age); // 自定义的逻辑方法
                    return new Student(name, age, school);
                })
                .collect(Collectors.toList());
 private static String getSchoolBasedOnAge(int age) {
    
    
        if (age < 18) {
    
    
            return "High School";
        } else {
    
    
            return "University";
        }
    }

8、collect(Collector<? super T, A, R> collector): 将流中的元素收集到一个集合或其他可变结果容器中。最简单的就是返回一个新的List,上面方法都是

List<String> list = Arrays.asList("apple", "banana", "cat");
List<String> collectedList = list.stream().collect(Collectors.toList());

8.1、collect(Collector<? super T, A, R> collector) 是 Stream API 中的一个终端操作方法,用于将流中的元素收集到一个集合或其他可变结果容器中。该方法接受一个Collector对象作为参数,用于定义收集元素的规则。Collector对象可以通过Collectors工厂类的静态方法创建,也可以自定义实现。

Collector<? super T, A, R> 中的泛型参数表示以下内容:
T:表示流中的元素类型。? super T 表示任何 T 类型的超类型,这允许 Collector 处理 T 类型及其子类型的元素。
A:表示用于累积流元素的可变结果容器(accumulator)的类型。在收集过程中,Collector 将逐个元素累积到 A 类型的容器中。
R:表示收集操作的最终结果类型。Collector 最终将累积的结果转换为 R 类型,并返回结果。
在 Collector<? super T, A, R> 中,? super T 允许处理 T 类型及其子类型的元素。这种设计使得 Collector 更加通用,可以处理不同类型的元素,而不仅仅局限于 T 类型。累积结果容器的类型 A,以及收集操作的最终结果类型 R

例如:常用的list转map

public static void main(String[] args) {
    
    
        List<Person> persons = Arrays.asList(
                new Person("Alice", 25),
                new Person("Bob", 30),
                new Person("Charlie", 35)
        );

        Map<String, Integer> nameToAgeMap = persons.stream()
                .collect(Collectors.toMap(Person::getName, Person::getAge));

        System.out.println(nameToAgeMap);
    }

转为< id:实体 >

//在toMap()方法中,我们传递了三个参数:Person::getName用于提取姓名作为键,Function.identity()
//用于将Person对象本身作为值,以及一个合并函数 (existingValue, newValue) -> newValue 来处理
//重复的键。在合并函数中,我们选择使用新值作为合并后的值。
public static void main(String[] args) {
    
    
        List<Person> persons = Arrays.asList(
                new Person("Alice", 25),
                new Person("Bob", 30),
                new Person("Alice", 35)
        );

        Map<String, Person> nameToPersonMap = persons.stream()
                .collect(Collectors.toMap(Person::getName, Function.identity(), (existingValue, newValue) -> newValue));

        System.out.println(nameToPersonMap);
    }

8.2 使用collect()方法分组,以年龄分组

public static void main(String[] args) {
    
    
        List<Person> persons = Arrays.asList(
                new Person("Alice", 25),
                new Person("Bob", 30),
                new Person("Charlie", 35),
                new Person("Dave", 25)
        );

        Map<Integer, List<Person>> ageGroupMap = persons.stream()
                .collect(Collectors.groupingBy(Person::getAge));

        System.out.println(ageGroupMap);
    }

多层分组,根据城市和年龄进行多级分组。

public static void main(String[] args) {
    
    
        List<Person> persons = Arrays.asList(
                new Person("Alice", "New York", 25),
                new Person("Bob", "London", 30),
                new Person("Charlie", "New York", 35),
                new Person("Dave", "London", 25)
        );

        Map<String, Map<String, List<Person>>> cityToAgeGroupMap = persons.stream()
                .collect(Collectors.groupingBy(Person::getCity,
                        Collectors.groupingBy(person -> person.getAge() < 30 ? "Young" : "Old")));

        System.out.println(cityToAgeGroupMap);

得到了一个名为resultMap的嵌套的Map,其中外层的键是姓名,内层的键是年龄,值是对应的Person对象。

public static void main(String[] args) {
    
    
        List<Person> persons = Arrays.asList(
                new Person("Alice", 25),
                new Person("Bob", 30),
                new Person("Charlie", 35),
                new Person("Dave", 25)
        );

        Map<String, Map<Integer, Person>> resultMap = persons.stream()
                .collect(Collectors.groupingBy(
                        Person::getName,
                        Collectors.toMap(
                                Person::getAge,
                                person -> person
                        )
                ));

        System.out.println(resultMap);
    }

还可以做这些事情:

统计和汇总:
Collectors.counting(): 统计流中元素的数量。
Collectors.summingInt(toIntFunction): 对流中的元素进行整数求和。
Collectors.averagingInt(toIntFunction): 计算流中元素的平均值。
Collectors.summarizingInt(toIntFunction): 生成流中元素的汇总统计结果,包括数量、总和、平均值、最大值和最小值。

字符串拼接和连接:
Collectors.joining(): 将流中的元素按照指定的分隔符连接成一个字符串。
Collectors.joining(delimiter, prefix, suffix): 将流中的元素按照指定的分隔符、前缀和后缀连接成一个字符串。

9、allMatch(Predicate<? super T> predicate): 检查流中的所有元素是否都满足给定条件。返回boolean

List<Integer> list = Arrays.asList(2, 4, 6);
boolean allEven = list.stream().allMatch(n -> n % 2 == 0);

10、skip(long n): 跳过流中的前n个元素。这个可以用于列表分页
代码如下:跳过当前页前面的元素,然后再通过limit限制每页的大小
在这里插入图片描述
11、reduce(BinaryOperator accumulator): 根据给定的累加器函数对流中的元素进行归约操作。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> sum = list.stream().reduce(0, (a, b) -> a + b);

找一个列表中年龄最大的Person

List<Person> people = Arrays.asList(
    new Person("John", 25),
    new Person("Alice", 30),
    new Person("Bob", 20)
);
Person oldestPerson = people.stream().reduce((p1, p2) -> p1.getAge() > p2.getAge() ? p1 : p2).orElse(null);

当然还可以做其他聚合和累积操作,具体在遇到聚合或者累加时,可以想到.reduce方法。

12、distinct(): 去除重复的元素。

List<String> list = Arrays.asList("apple", "banana", "apple");
Stream<String> distinctStream = list.stream().distinct();

13、flatMap(Function<? super T, ? extends Stream<? extends R>> mapper): 将流中的每个元素进行映射后扁平化成一个流。这个也是很好用的方法

List<List<Integer>> nestedList = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4));
List<Integer> flattenedList = nestedList.stream().flatMap(Collection::stream).collect(Collectors.toList());

13.1、flatMap() 方法在处理嵌套结构、处理多个集合或流之间的映射关系时非常有用。它可以将嵌套的集合或流展开为一个更简单的结构,方便后续的处理和操作。

List<List<List<String>>> nestedList = Arrays.asList(
    Arrays.asList(
        Arrays.asList("apple", "banana"),
        Arrays.asList("cherry", "date")
    ),
    Arrays.asList(
        Arrays.asList("grape", "kiwi"),
        Arrays.asList("lemon", "mango")
    )
);

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

System.out.println(flattenedList); // 输出:[apple, banana, cherry, date, grape, kiwi, lemon, mango]

假设有一个 Person 类型,其中每个人可以有多个爱好(hobbies)。每个爱好又可以有多个标签(tags)。我们可以使用 flatMap() 方法来处理这种嵌套的引用类型结构。

public static void main(String[] args) {
    
    
        List<Person> people = Arrays.asList(
                new Person("John", Arrays.asList("Reading", "Painting")),
                new Person("Alice", Arrays.asList("Music", "Cooking", "Hiking")),
                new Person("Bob", Arrays.asList("Photography"))
        );

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

        System.out.println(allHobbies);
    }

使用 flatMap() 方法,我们将 people 列表中每个人的爱好(List)流化,并将它们扁平化成一个单一的流。最后,我们将流收集为一个列表 allHobbies,其中包含所有人的爱好。

后续有更好用的再补充。

猜你喜欢

转载自blog.csdn.net/qq_40454136/article/details/131576878