第05篇 强大的Stream

本文深入探讨了Java8中引入的Stream API,讲解了Stream的基本概念、特点及操作流程,包括创建流的方法、中间操作与终止操作的使用,并通过实例演示了如何利用Stream API高效处理数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、Stream介绍

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对 集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数 据库查询。也可以使用 Stream API 来并行执行操作。简而言之, Stream API 提供了一种高效且易于使用的处理数据的方式。
总之:流(Stream)是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”

二、Stream特点

  1. Stream 自己不会存储元素。
  2. Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  3. Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
  4. Stream和迭代器一样,只能遍历一次,如果需要再次使用,可以从原始数据重新再获取一个流。

三、Stream的三个步骤

(一)总体介绍

  1. 创建 Stream
    使用一个数据源(如:集合、数组),获取一个流
  2. 中间操作
    一个中间操作链,对数据源的数据进行处理
  3. 终止操作(终端操作)
    一个终止操作,执行中间操作链,并产生结果
    在这里插入图片描述

(二)创建流

创建流主要有4中方法,分别如下:

1. 使用集合创建流

在java8中Collection接口被扩展,提供了两个方法返回流,如下:
default Stream stream() : 返回一个顺序流
default Stream parallelStream() : 返回一个并行流

2. 使用数组创建流

Java8 中的 Arrays 的静态方法 stream() 可 以获取数组流,有一些重载形式,能够处理对应基本类型的数组
static <T> Stream<T> stream(T[] array): 返回一个流
对于基本类型派生出了下面三个方法

  • public static IntStream stream(int[] array)
  • public static LongStream stream(long[] array)
  • public static DoubleStream stream(double[] array)
    另外,Arrays还有一个重载的两个参数的方法:
    public static <T> Stream<T> stream(T[] array, int startInclusive, int endExclusive)
    这个方法会对数组截断之后返回一个子数组的流

3. 由值创建流

可以使用静态方法 Stream.of(), 通过显示值 创建一个流。它可以接收任意数量的参数
public static<T> Stream<T> of(T... values)

4. 由函数创建流

  • 生成:public static<T> Stream<T> generate(Supplier<T> s)
    生成一个无限长度的Stream,其元素的生成是通过给定的Supplier(这个接口可以看成一个对象的工厂,每次调用返回一个给定类型的对象)
  • 迭代:public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
    生成无限长度的Stream,和generator不同的是,其元素的生成是重复对给定的种子值(seed)调用用户指定函数来生成的

5. 示例

public void createStream() {
  //1.通过Collection集合提供的stream()方法获取流
  List<String> sList = new ArrayList<>();
  Stream<String> stream = sList.stream();
  //2.通过Arrays.stream(数组)获取
  String[] array = new String[10];
  Stream<String> stream2 = Arrays.stream(array);
  //3.通过Stream.of()方法获取
  Stream<String> stream3 = Stream.of("Hello");
  //4.通过函数产生流
  //1).迭代:生成20,21,22,23 ....
  Stream<Integer> stream4 = Stream.iterate(20, x -> x + 1);
  //2).生成:一直生成100以内的随机数
  Stream<Integer> stream5 = Stream.generate(() -> new Random().nextInt(100));
}

(三)中间操作

多个中间操作可以连接起来形成一个流水线,除非流水 线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值”

中间操作有如下这些:
在这里插入图片描述
代码示例:

public class StreamDriver {
    List<Employee> elist = Arrays.asList(
        new Employee("aa", 45, 8888.88),
        new Employee("aa", 45, 8888.88),
        new Employee("aa", 45, 8888.88),
        new Employee("bb", 32, 6666.66),
        new Employee("cc", 47, 7777.77),
        new Employee("dd", 89, 5555.55),
        new Employee("ee", 12, 9999.99));
	@Test
	public void middleOp() {
		System.out.println("--------查找年龄大于40的人-----");
		System.out.println();
		Stream<Employee> filter = elist.stream().filter(x -> x.getAge() > 40);
		filter.forEach(System.out::println);

		System.out.println("-----去重,去掉信息相同的人---------");
		Stream<Employee> distinct = elist.stream().distinct();
		distinct.forEach(System.out::println);

		System.out.println("------跳过2个元素后取2个元素-----------");
		elist.stream().skip(2).limit(2).forEach(System.out::println);

		System.out.println("------打印每个人的姓名--------------");
		elist.stream().map((x) -> x.getName()).forEach(System.out::println);

		System.out.println("-----打印每个人的姓名每个字符为一行-----------");
		System.out.println("--------1.使用map");

		//获取到的是一个流的流
		Stream<Stream<Character>> map = elist.stream().map(x -> getCharStram(x.getName()));
		map.forEach(x -> x.forEach(System.out::println));

		//获取到的是一个合并后的流
		System.out.println("--------2.使用flatMap");
		Stream<Character> flatMap = elist.stream().flatMap(x -> getCharStram(x.getName()));
		flatMap.forEach(System.out::println);

		System.out.println("------按年龄排序------");
		Stream<Employee> sorted = elist.stream().sorted((x, y) -> x.getAge().compareTo(y.getAge()));
		sorted.forEach(System.out::println);
	}

	/**
	 * 通过一个字符串获取Stream<Character>
	 * @param str
	 * @return
	 */
	private Stream<Character> getCharStram(String str) {
		List<Character> cList = new ArrayList<>();
		for (char c : str.toCharArray()) {
			cList.add(c);
		}
		return cList.stream();
	}
}

结果如下:
--------查找年龄大于40的人-----
Employee [name=aa, age=45, salary=8888.88]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=cc, age=47, salary=7777.77]
Employee [name=dd, age=89, salary=5555.55]
-----去重,去掉信息相同的人---------
Employee [name=aa, age=45, salary=8888.88]
Employee [name=bb, age=32, salary=6666.66]
Employee [name=cc, age=47, salary=7777.77]
Employee [name=dd, age=89, salary=5555.55]
Employee [name=ee, age=12, salary=9999.99]
------跳过2个元素后取2个元素-----------
Employee [name=aa, age=45, salary=8888.88]
Employee [name=bb, age=32, salary=6666.66]
------打印每个人的姓名--------------
aa
aa
aa
bb
cc
dd
ee
-----打印每个人的姓名每个字符为一行-----------
--------1.使用map
a
a
a
a
a
a
b
b
c
c
d
d
e
e
--------2.使用flatMap
a
a
a
a
a
a
b
b
c
c
d
d
e
e
------按年龄排序------
Employee [name=ee, age=12, salary=9999.99]
Employee [name=bb, age=32, salary=6666.66]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=aa, age=45, salary=8888.88]
Employee [name=cc, age=47, salary=7777.77]
Employee [name=dd, age=89, salary=5555.55]

(四)终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。
相关方法如下:
在这里插入图片描述
Collector 接口中方法的实现决定了如何对流执行收集操作(如收 集到 List、Set、Map)。但是 Collectors 实用类提供了很多静态 。
在这里插入图片描述
示例代码

/**
 * 终止操作
 */
@Test
public void finalOp() {
    //检查是否所有的人年龄大于30
    boolean allMatch = elist.stream().allMatch(x -> x.getAge() > 30);
    System.out.println("是否所有的人年龄大于30 : " + allMatch);
    boolean anyMatch = elist.stream().anyMatch(x -> x.getAge() > 30);
    System.out.println("查看是否有年龄大于30的:" + anyMatch);
    Optional<Employee> first = elist.stream().findFirst();
    Employee employee = first.get();
    System.out.println("第一个元素是:" + employee);
    Optional<Employee> max = elist.stream().max((x, y) -> x.getSalary().compareTo(y.getSalary()));
    System.out.println("最高工资是:" + max.get().getSalary());

    Optional<Employee> reduce = elist.stream().reduce((x, y) -> {
        double s = x.getSalary() + y.getSalary();
        x.setSalary(s);
        return x;
    });
    System.out.println("工资总和为:" + reduce.get().getSalary());
}

/**
 * 收集
 */
@Test
public void collect() {
    //找出集合中年龄大于30岁的,放入到一个新的集合
    List<Employee> elist2 = elist.stream().filter(x -> x.getAge() > 30).collect(Collectors.toList());
    //统计工资大于7000的人数
    Long c = elist.stream().filter(x -> x.getSalary() > 7000).collect(Collectors.counting());
    System.out.println("年龄大于7000的人数为:" + c);

    Double avgAge = elist.stream().collect(Collectors.averagingInt(x -> x.getAge()));
    System.out.println("平均年龄是:" + avgAge);

    String collect = elist.stream().map(x -> x.getName()).collect(Collectors.joining(","));
    System.out.println("所有的姓名是:" + collect);

}

结果:
是否所有的人年龄大于30 : false
查看是否有年龄大于30的:true
第一个元素是:Employee [name=aa, age=45, salary=8888.88]
最高工资是:9999.99
工资总和为:56666.61000000001
年龄大于7000的人数为:5
平均年龄是:45.0
所有的姓名是:aa,aa,aa,bb,cc,dd,ee

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值