我将继续感谢黑马程序员的 java 课程。

初爽 Stream 流

需求:这里有一个放着一些姓名的集合,我需要将姓张的、名字长度为三的姓名放进新集合并打印。

一般来说,我们可以写两个遍历用 startWith 等方法找到对应条件的姓名并存入新集合。但这很麻烦。

我们可以直接使用 Stream 流实现一个过滤:

import java.util.ArrayList;

public class demo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三");
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张三丰");

        list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3).forEach(name -> System.out.println(name));
    }
}

输出:

张无忌
张三丰

我们就完成了从“怎么做”到“做什么”的转变。

注意filter 是惰性操作,只有在遇到 forEach 这样的终端操作时才会真正执行处理。

概述

Stream可以由数组或集合创建,对流的操作分为两种:

  1. 中间操作,每次返回一个新的流,可以有多个。(筛选 filter、映射 map、排序 sorted、去重组合 skip—limit)
  2. 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。(遍历 foreach、匹配 find–match、规约 reduce、聚合 max–min–count、收集 collect)

特性:

  • stream 不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  • stream 不会改变数据源,通常情况下会产生一个新的集合或一个值。
  • stream 具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

Stream 可以传递数组,但如果数组内的数据是基本数据类型数据,会将数组作为一个元素放入 Stream。所以如果将一个 int[] 放进 stream 再 forEach 遍历,得到的结果只是一个地址值。

方法

常用的中间方法

filter

用于通过设置的条件过滤出元素。

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();

limit

用于获取指定数量的流。

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

skip(long n)

跳过流中的前 n 个元素,返回由剩余元素组成的新流。如果流中元素不足 n 个,则返回空流。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
       .skip(2)                // 跳过前2个元素:1,2
       .forEach(System.out::println);
// 输出:3  4  5

distinct()

除流中的重复元素,底层依赖元素的 equals() 和 hashCode() 方法判断是否相同。返回一个去重后的新流。

  • 对于自定义对象,要确保正确重写 equals 和 hashCode,否则去重可能无效。
  • 是一个 有状态 的中间操作(需要记住之前出现过的元素)。
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 1, 4);
numbers.stream()
       .distinct()             // 去重
       .forEach(System.out::println);
// 输出:1  2  3  4  (顺序保持首次出现的顺序)

concat(Stream a, Stream b)

  • 两个流必须是同类型的(或子类)。
  • 合并后的流是 按顺序 的:全部 a 元素 → 全部 b 元素
Stream<String> streamA = Stream.of("A", "B", "C");
Stream<String> streamB = Stream.of("D", "E");
Stream<String> result = Stream.concat(streamA, streamB);
result.forEach(System.out::print);
// 输出:ABCDE

map

将流中的每个元素按照给定的映射函数转换成另一种形式(或提取某个属性),返回一个由转换后元素组成的新流。

// 将字符串转换成其长度
List<String> words = Arrays.asList("Java", "Stream", "Map");
words.stream()
     .map(String::length)
     .forEach(System.out::println);
// 输出:4  6  3

// 将数字转换成其平方
Stream.of(1, 2, 3)
      .map(n -> n * n)
      .forEach(System.out::print);   // 输出:149

常用终结方法

forEach

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

count

返回集合元素个数。

List<String> list = Arrays.asList("猫", "狗", "鸟");
long n = list.stream().count();
System.out.println(n);   // 输出 3

注意:count() 返回的是 long 类型

收集(toArray、collect)

toArray()

收集流中的数据,并放入数组。

List<String> list = Arrays.asList("苹果", "香蕉", "橙子");
String[] arr = list.stream().toArray(String[]::new);
System.out.println(Arrays.toString(arr));  // [苹果, 香蕉, 橙子]

对于 toArray()

  • 如果流里面是字符串,String[]::new
  • 如果流里面是整数,Integer[]::new

collect()

收集流中的数据,并放入集合。

List<String> list = Arrays.asList("红", "黄", "蓝", "红");
List<String> result = list.stream()
                          .filter(s -> s.startsWith("红"))
                          .collect(Collectors.toList());
System.out.println(result);  // [红, 红]  List 数据可重复
Set<String> result = list.stream()
                         .filter(s -> s.startsWith("红"))
                         .collect(Collectors.toSet());
System.out.println(result);  // [红]  只有一个,重复的自动去掉

注意:用 collect 需要导入 java.util.stream.Collectors

  • reward_image1
此作者没有提供个人介绍。
最后更新于 2026-05-02