java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java stream详解

Java的Stream入门级教程

作者:不吃肘击

Java的Stream(流)是从Java 8 引入的一套面向集合数据的声明式处理API,它让你像写“数据流水线”一样,对一组元素做筛选、变换、聚合等操作,代码更简洁、更可读,本文给大家介绍Java的Stream入门级教程,感兴趣的朋友跟随小编一起看看吧

什么是Stream流

Java 的 Stream(流)是从 Java 8 引入的一套面向集合数据的声明式处理API。它让你像写“数据流水线”一样,对一组元素做筛选、变换、聚合等操作,代码更简洁、更可读。

核心特性

管道化source → 中间操作* → 终止操作

惰性求值:只有遇到终止操作才真正执行。

无副作用:鼓励函数式写法,避免改动外部可变状态。

可并行parallelStream() 能并行处理(适合大数据量且拆分成本低的场景)。

Stream流不是数据本身,而是数据的视图,且Stream不可重复使用,且基于函数式接口

Stream流的生命周期

Java中Stream流的生命周期分三个步骤创建 → 中间操作 → 终止操作即生成数据,处理数据到最后执行结果

创建流

源:流是基于数据源如集合,数组,文件创建的,数据源可以是集合,数组,生成器等

例子:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Stream<Integer> stream = numbers.stream();  // 创建流

中间操作

惰性执行:中间操作是惰性求值的,他们不会立即执行。而是返回一个新的流,只有遇到终止操作的时候,中间操作才会执行。

例子:

Stream<Integer> evenNumbers = stream.filter(n -> n % 2 == 0)  // 中间操作
                                   .map(n -> n * n);          // 另一个中间操作

终止操作

触发执行:终止操作会触发流的处理链执行,一旦执行终止操作,流就会被消耗掉,而不能再使用.终止操作是流处理的真正的启动器,他会返回一个最终的结果

int sum = evenNumbers.reduce(0, Integer::sum);  // 终止操作,执行计算

流的关闭

一旦流被消耗掉就不能再被重新使用,当要再次处理数据的时候就要创建一个新的流

在关闭流的资源上对于集合类的流就不需要显示关闭但是对于文件流,网络流等最好通过close()来关闭

Stream流的创建方式

list.stream()

从集合创建
Arrays.stream(array)从数组创建
Stream.of("A","B","C")从值创建
Files.lines(Path.get("file.txt"))从文件行创建
Stream.iterate(0,n->n+1),Stream.generate(Math::random)无限创建流

常见的中间操作

filter():过滤符合的条件stream.filter(s -> s.length() > 3)
map(Function)转换每个元素或者是提取对象的某个是属性。将原始流中的每个元素映射为另一个类型或值。例子:steam.map(String::toUpperCase)
flatMap(Function)扁平化处理,用于嵌套结构例子:stream.flatMap(List::stream)
distinct()去重(基于equals()和hashCode()方法)例子:stream.distinct()
sorted()排序(自然排序或者是自定义排序)例子:stream.sorted(Comparator.reverseOrder())
liit(n)截取前n个元素例子:stream.limit(10)

skip(n)

跳过前n个元素stream.skip(5)

:

:

:


 

常用的终端操作

forEach(Consumer)遍历每个元素stream.forEach(System.out::println)
collect(Collector)收集结果(转为 List、Set、Map 等)stream.collect(Collectors.toList())
count()返回元素个数stream.count()
anyMatch(Predicate)是否有任意一个匹配stream.anyMatch(s -> s.contains("Java"))
allMatch(Predicate)是否全部匹配stream.allMatch(s -> s.length() > 3)
noneMatch(Predicate)是否没有匹配stream.noneMatch(s -> s.isEmpty())
findFirst()获取第一个元素stream.findFirst()
reduce(BinaryOperator)聚合操作(如求和)stream.reduce((a, b) -> a + b)

注意:中间操作可以有多个,但是在一个stream流中流的创建和终止操作只能有一个

实战用例

1.将一个对象列表中大于10岁的对象的名字提取出来

首先给定一个Person类(在实际开发中不建议将属性定义为public,这里只是为了演示

public class Person {
    public String name;
    public int age;
    public String city;
    public Person(String name,int age,String city){
        this.name = name;
        this.age = age;
        this.city = city;
    }
}

代码演示:

import java.util.List;
import java.util.stream.Collectors;
public class StreamTest {
    public static void main(String []str){
        List<Person> people = List.of(
                new Person("Alice", 23, "Beijing"),
                new Person("Bob", 17, "Shanghai"),
                new Person("Carol", 30, "Beijing")
        );
        //提取大于18岁的对象的名字
        List<String> collect = people.stream()//获取源数据
                .filter(p -> p.age > 18)//过滤源数据,满足条件则true则就保留
                .map(person -> person.name)//做map映射就是将这个源数据中的每个数据转换成另外一种数据,
                // 在这里面就是将每个对象转换成他们对应的名字
                .collect(Collectors.toList());//收集处理后的数据,通过列表的方式
        System.out.println(collect);
    }
}

2.分组统计,将列表中不同城市的人的数量做统计

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class StreamTest {
    public static void main(String []str){
        List<Person> people = List.of(
                new Person("Alice", 23, "Beijing"),
                new Person("Bob", 17, "Shanghai"),
                new Person("Carol", 30, "Beijing"),
                new Person("Dave", 25, "Shanghai"),
                new Person("Eve", 22, "Beijing")
        );
        Map<String, Long> collect = people.stream()//转为流数据
                .collect(Collectors.groupingBy(Person::getCity, Collectors.counting()));
        //这个的作用是将这个流中的数据做一个分组,key是city,value是key对应的数量Collectors.counting() 是一个下游收集器,用来统计分组后的元素数量。
        //它返回一个 Long 类型的值,表示每个分组中元素的数量。在这里,它会统计每个城市中有多少个 Person 对象。
        System.out.println(collect);
    }
}

3.多级分组-分区(在城市分组的情况下判断是否成年)

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class StreamTest {
    public static void main(String []str){
        List<Person> people = List.of(
                new Person("Alice", 23, "Beijing"),
                new Person("Bob", 17, "Shanghai"),
                new Person("Carol", 30, "Beijing"),
                new Person("Dave", 25, "Shanghai"),
                new Person("Eve", 22, "Beijing")
        );
        Map<String, Map<Boolean, List<Person>>> collect = people.stream()
                .collect(Collectors.groupingBy(Person::getCity, Collectors.partitioningBy(p -> p.age > 18)));
                //这里面的Collectors.groupingBy是将收集的数据进行分组根据city属性,他收集出来就是一个map,但是这个map的value不是一个值
        //而是一个对象这个对象是一个分区的函数partitioningBy他会将value又分成一个map将满足条件的放一组,不满足的放一组来形成一个map
        System.out.println(collect);
    }

4.聚合reduce

在java中的stream流的reduce方法是一个游泳的规约操作,将流中的元素反复结合起来最后得到一个值

reduce求和

import java.util.Arrays;
import java.util.List;
public class StreamTest {
    public static void main(String []str){
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        int sum = numbers.stream()
                .reduce(0, (a, b) -> a + b);  // 0 是初始值,(a, b) -> a + b 是累加器
        System.out.println(sum);  // 输出 15
    }
}

reduce求最大值

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> max = numbers.stream()
                               .reduce(Integer::max);  // 使用 Integer::max 方法引用
max.ifPresent(System.out::println);  // 输出 5

在求最大值的时候就不需要初始值了

reduce拼接字符串

List<String> words = Arrays.asList("Java", "is", "awesome");
String result = words.stream()
                     .reduce("", (a, b) -> a + " " + b);  // 初始值为空字符串
System.out.println(result);  // 输出 "Java is awesome"

5.统计年龄总数

IntSummaryStatistics stats = people.stream()
    .collect(Collectors.summarizingInt(Person::age));
// stats.getCount()/getSum()/getMin()/getMax()/getAverage()

6.自定义排序

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class StreamTest {
    public static void main(String []str){
        List<Person> people = List.of(
                new Person("Alice", 23, "Beijing"),
                new Person("Bob", 17, "Shanghai"),
                new Person("Carol", 30, "Beijing"),
                new Person("Dave", 25, "Shanghai"),
                new Person("Eve", 22, "Beijing")
        );
        Comparator<Person> byCityThenAge = Comparator
                .comparing(Person::getCity, Comparator.nullsLast(String::compareTo))//定义比较类先是通过city排序,对于null的排到最后
                .thenComparingInt(Person::getAge);//然后通过年龄进行比较
        List<Person> sorted = people.stream()
                .sorted(byCityThenAge)//通过比较类进行排序
                .collect(Collectors.toList());
        System.out.println(sorted);
    }
}

怎么使用Comparator

Comparator 是 Java 中用于比较对象的接口,常用于排序操作。它定义了如何对对象进行排序,并且可以自定义排序的规则。可以通过 Comparator 来指定对象之间的比较逻辑,并且与 Collections.sort() 或 Stream.sorted() 等方法配合使用。

Comparator接口常用方法:

多条件排序

List<Person> people = Arrays.asList(
    new Person("John", 25),
    new Person("Alice", 30),
    new Person("Bob", 20)
);

// 按名字排序,如果名字相同再按年龄排序
people.sort(Comparator.comparing(Person::getName)
                      .thenComparing(Person::getAge));

逆序排序

// 按年龄降序排序
people.sort(Comparator.comparing(Person::getAge).reversed());

使用 Comparator.nullsLast() 处理 null 值

List<String> strings = Arrays.asList("apple", "banana", null, "cherry", null);
strings.sort(Comparator.nullsLast(Comparator.naturalOrder())); // 排序时,null 值排在最后
strings.forEach(System.out::println);

使用Comparing排序

// 按年龄排序,使用静态方法 Comparator.comparing
people.sort(Comparator.comparing(Person::getAge)); // 按年龄升序

使用Lambda表达式排序

// 使用 Lambda 表达式简化比较逻辑
Collections.sort(people, (p1, p2) -> Integer.compare(p1.getAge(), p2.getAge())); // 按年龄升序

Stream流的优势?

1.简洁性和可读性

2.避免显式的循环

3.并行化和性能优化

4.惰性计算和延迟求值

5.函数式编程思想

6.增强的可组合性和复用性

7.更好的错误处理

8.数据转换和聚合的高级功能

9.可维护性和扩展性

10.内存使用优化

总结:Stream API 的优势

相比于传统的开发方式,Java Stream API 提供了以下几大优势:

到此这篇关于Java的Stream详解的文章就介绍到这了,更多相关java stream内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文