Stream 的方法

Stream 中的方法分为三类:

  1. 静态方法:用于创建流
  2. 中间操作:通常是将一种流转换成另一种流
  3. 终止操作:通常是对流中的元素进行计算,得出一个结果

流是惰性求值的,只有执行终止操作时才会真正执行计算。

创建流

empty

生成一个空的流

public static void empty() {
    Stream.empty().forEach(System.out::println);
}

of

生成包含指定元素的串行流

public static void of() {
    Stream.of("a").forEach(System.out::println);
    Stream.of("a", "b", "c").forEach(System.out::println);
}
a
a
b
c

iterate

生成一个无限的串行流,流的第一个元素是 seed,之后的元素都是对前一个元素应用函数 f 得到的。形如 seed、f(seed)、f(f(seed)) …

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);
public static void iterate() {
    // 由于是无限的流,所以需要 limit 
    Stream.iterate(1, (e) -> e * 2)
            .limit(10)
            .forEach(System.out::println);
}
1
2
4
8
16
32
64
128
256
512

generate

生成一个无限的串行流,流中的元素都是函数 s 提供的。通常用于生成常量流、随机元素流等。

public static<T> Stream<T> generate(Supplier<T> s);
public static void generate() {
    // 由于是无限的流,所以需要 limit
    Stream.generate(() -> 1)
        .limit(5)
        .forEach(System.out::println);

    System.out.println();

    Random random = new Random();
    Stream.generate(random::nextInt)
        .limit(5)
        .forEach(System.out::println);
}
1
1
1
1
1

-1986698800
-1639744933
-172603197
-393447923
1674768852

concat

连接两个流

public static void concat() {
    Stream<String> stream1 = Stream.of("a");
    Stream<String> stream2 = Stream.of("b", "c");

    Stream.concat(stream1, stream2).forEach(System.out::println);
}
a
b
c

builder

使用 Stream.Builder 构建流

public static void builder() {
    Stream.Builder<String> builder = Stream.builder();
    Stream<String> stream = builder
            .add("a")
            .add("b")
            .add("c")
            .build();
    stream.forEach(System.out::println);
}
a
b
c

中间操作

filter

过滤元素,只有 predicate 为 true 的元素会进入下游的流

Stream<T> filter(Predicate<? super T> predicate);
public static void filter() {
    Stream.of("java", "python", "javascript")
            .filter((e) -> e.startsWith("j"))
            .forEach(System.out::println);
}
java
javascript

map、mapToInt、mapToLong、mapToDouble

返回一个流,包含给定函数应用于流中每一个元素后的结果。可用于将一种流 T 转换为另一种流 R,或者只操作元素,不改变流的类型。为了避免频繁自动拆箱、装箱的损耗,提供了针对基本类型的方法 mapToInt、mapToLong、mapToDouble

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

IntStream mapToInt(ToIntFunction<? super T> mapper);

LongStream mapToLong(ToLongFunction<? super T> mapper);

DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
public static void map() {
    Stream<Integer> stream1 = Stream.of("java", "python", "javascript")
            .map(String::length);
    stream1.forEach(System.out::println);

    IntStream intStream = Stream.of("java", "python", "javascript")
            .mapToInt(String::length);
    intStream.forEach(System.out::println);
}
4
6
10
4
6
10

flatMap、flatMapToInt、flatMapToLong、flatMapToDouble

mapper 函数的返回值是 Stream。对流中的所有元素应用函数 mapper,然后将其结果流中的元素放入新的流,然后返回这个新流。多用于处理一对多的关系:对流的元素应用一对多转换,然后将生成的元素平展成一个新的流。同样提供了针对基本类型的方法。

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);

LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);

DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);

假设有订单列表 List<Order> orders,订单中有商品列表 List<Item> items,我们可以用以下的方式生成单个 Stream<Item> 并处理商品。

@Data
@AllArgsConstructor
static class Item {
    private String name;

    public static Item of(String name) {
        return new Item(name);
    }
}

@Data
@AllArgsConstructor
static class Order {
    private List<Item> items;

    public static Order of(Item... items) {
        return new Order(Arrays.asList(items));
    }
}

public static void flatMap() {
    Item apple = Item.of("Apple");
    Item banana = Item.of("Banana");
    Item peach = Item.of("Peach");
    Item lemon = Item.of("Lemon");

    Order order1 = Order.of(apple, banana);
    Order order2 = Order.of(peach, lemon);

    List<Order> orders = Arrays.asList(order1, order2);
    // 生成单个 Stream<Item>,而不是多个
    Stream<Item> itemStream = orders.stream().flatMap(order -> order.getItems().stream());
    
    itemStream.forEach(System.out::println);
}
T1.Item(name=Apple)
T1.Item(name=Banana)
T1.Item(name=Peach)
T1.Item(name=Lemon)

distinct

利用 equals 方法进行去重。

public static void distinct() {
    Stream.of("a", "b", "c", "a", "b")
            .distinct()
            .forEach(System.out::println);
}
a
b
c

sorted

// 以自然序排序,元素必须都实现了接口 Comparable
Stream<T> sorted();
// 以指定的比较器排序
Stream<T> sorted(Comparator<? super T> comparator);
public static void sorted() {
    // 自然序
    Stream.of("sql", "python", "java")
            .sorted()
            .forEach(System.out::println);

    System.out.println();

    // 字符长度
    Stream.of("sql", "python", "java")
            .sorted(Comparator.comparingInt(String::length))
            .forEach(System.out::println);
}
java
python
sql

sql
java
python

peek

对流中的元素应用函数 action(可以修改元素,也可以只是简单的打印),然后返回当前流,与 map 不同,peek 不会生成新的流。

Stream<T> peek(Consumer<? super T> action);
public static void peek() {

    Item apple = Item.of("Apple");
    Item banana = Item.of("Banana");
    Item peach = Item.of("Peach");
    Item lemon = Item.of("Lemon");
    Stream.of(apple, banana, peach, lemon)
            .filter(e -> e.getName().length() < 6)
            // 打印
            .peek(System.out::println)
            // 修改名称
            .peek(e -> e.setName(e.getName() + "__"))
            .forEach(System.out::println);
}
T1.Item(name=Apple)
T1.Item(name=Apple__)
T1.Item(name=Peach)
T1.Item(name=Peach__)
T1.Item(name=Lemon)
T1.Item(name=Lemon__)

limit

截取前 N 个元素,丢弃后面的元素。

public static void limit() {
    Stream.of("a", "b", "c", "d", "e")
            .limit(3)
            .forEach(System.out::println);
}
a
b
c

skip

跳过前 N 个元素

public static void skip() {
    Stream.of("a", "b", "c", "d", "e")
            .skip(3)
            .forEach(System.out::println);
}
d
e

终止操作

forEach、forEachOrdered

对于串行流来说,两个方法的行为是一样的。但对于并行流:

  • forEach:行为是不确定的,元素可能以任意顺序被操作
  • forEachOrdered:元素以添加的顺序被操作,这样牺牲了并行性的好处
public static void forEach() {
    // 串行流
    Stream.of("a", "b", "c", "d", "e")
            .forEach(e -> System.out.print(e + " "));

    System.out.println();

    Stream.of("a", "b", "c", "d", "e")
            .forEachOrdered(e -> System.out.print(e + " "));

    System.out.println();

    // 并行流
    Stream.of("a", "b", "c", "d", "e")
            .parallel()
            .forEach(e -> System.out.print(e + " "));

    System.out.println();

    Stream.of("a", "b", "c", "d", "e")
            .parallel()
            .forEachOrdered(e -> System.out.print(e + " "));
}

下面是两次的运行结果。对于串行流,两个方法的结果是一样的。对于并行流,每次执行 forEach 的结果可能不一样,但执行 forEachOrdered 的结果肯定是一样的。

a b c d e 
a b c d e 
c d e b a 
a b c d e 
--------------
a b c d e 
a b c d e 
c e d b a 
a b c d e 

toArray

// Object 数组
Object[] toArray();

// 传入一个 new 数组的 generator,参数为数组的 length,且返回值是泛型数组
<A> A[] toArray(IntFunction<A[]> generator);
public static void  toArray() {
    Object[] objectArray = Stream.of("a", "b", "c").toArray();

    String[] stringArray1 = Stream.of("a", "b", "c")
            .toArray(length -> {
                System.out.println(length);
                return new String[length];
            });

    // 上面可以简化为
    String[] stringArray2 = Stream.of("a", "b", "c").toArray(String[]::new);


    System.out.println(Arrays.toString(objectArray));
    System.out.println(Arrays.toString(stringArray1));
    System.out.println(Arrays.toString(stringArray2));
}
3
[a, b, c]
[a, b, c]
[a, b, c]

reduce

// 注意:流中元素的类型为 T

T reduce(T identity, BinaryOperator<T> accumulator);
// 等价于以下代码,但不按顺序操作元素
     T result = identity;
     for (T element : this stream)
         result = accumulator.apply(result, element)
     return result;

// 与上面的区别在于没有提供初始值
Optional<T> reduce(BinaryOperator<T> accumulator);
// 等价于以下代码,但不按顺序操作元素
     boolean foundAny = false;
     T result = null;
     for (T element : this stream) {
         if (!foundAny) {
             foundAny = true;
             result = element;
         }
         else
             result = accumulator.apply(result, element);
     }
     return foundAny ? Optional.of(result) : Optional.empty();

// 注意:初始值为 U 类型,不是 T 类型
// identity 是初始值
// accumulator 是累加器,用于将元素累加到 result 中
// combiner 是多线程最后合并结果的,比如 toList 就是将两个 List 合并成一个 List 的
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
// 等价于以下代码,但不按顺序操作元素
     U result = identity;
     for (T element : this stream)
         result = accumulator.apply(result, element)
     return result;

实现 sum

public static void reduceSum() {
    // 实现 sum
    Integer sum1 = Stream.of(1, 2, 3).reduce(0, (a, b) -> a + b);

    Integer sum2 = Stream.of(1, 2, 3).reduce(0, Integer::sum);
    System.out.println(sum1);
    System.out.println(sum2);
}
6
6

实现 max

public static void reduceMax() {
    // 实现 max
    Optional<Integer> max1 = Stream.of(1, 2, 3).reduce((a, b) -> Integer.max(a, b));
    Optional<Integer> max2 = Stream.of(1, 2, 3).reduce(Integer::max);

    System.out.println(max1);
    System.out.println(max2);
}
Optional[3]
Optional[3]

实现 toList

public static void reduceToList() {
    // 实现 toList
    List<Integer> result = Stream.of(1, 2, 3)
            .reduce(
                    new ArrayList<>(), 
                    (list, e) -> {list.add(e); return list;}, 
                    (list1, list2) -> {list1.addAll(list2); return list1;}
            );
    System.out.println(result);
}
[1, 2, 3]

collect

Collectors 各个方法的使用,请移步 Collectors 中的各种方法

<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
// 等价于以下代码,
     R result = supplier.get();
     for (T element : this stream)
         accumulator.accept(result, element);
     return result;

// Collector 封装了上面的 3 个参数,并提供了其他的参数
<R, A> R collect(Collector<? super T, A, R> collector);

实现 toList

public static void  collectToList() {
    // 实现 toList
    List<Integer> list1 = Stream.of(1, 2, 3)
            . collect(
                    ArrayList::new,
                    ArrayList::add,
                    ArrayList::addAll
            );

    List<Integer> list2 = Stream.of(1, 2, 3)
            .collect(Collectors.toList());

    System.out.println(list1);
    System.out.println(list2);
}
[1, 2, 3]
[1, 2, 3]

min、max

根据传入的比较器,求最大、最小值。流是空的,会返回 Optional.empty()

Optional<T> min(Comparator<? super T> comparator);

Optional<T> max(Comparator<? super T> comparator);
public static void min() {
    // 自然序
    Optional<String> min = Stream.of("sql", "python", "java")
            .min(Comparator.comparingInt(String::length));

    System.out.println(min);
}

count

public static void count() {
    long count = Stream.of("java", "python", "javascript")
            .filter((e) -> e.startsWith("j"))
            .count();

    System.out.println(count);
}
2

anyMatch、allMatch、noneMatch

// 是否有元素符合条件
boolean anyMatch(Predicate<? super T> predicate);

// 是否所有元素都符合条件
boolean allMatch(Predicate<? super T> predicate);

// 是否所有元素都不符合条件
boolean noneMatch(Predicate<? super T> predicate);
private static void match() {
    boolean anyMatch = Stream.of(1, 2, 3)
            .anyMatch(e -> e > 2);

    boolean allMatch = Stream.of(1, 2, 3)
            .allMatch(e -> e > 0);

    boolean noneMatch = Stream.of(1, 2, 3)
            .noneMatch(e -> e > 0);

    System.out.println(anyMatch);
    System.out.println(allMatch);
    System.out.println(noneMatch);
}
true
true
false

findFirst、findAny

  • findFirst:无论是串行流还是并行流,始终返回第一个
  • findAny:返回任意一个元素。对于并行流,每次的结果可能不一样
private static void find() {
    // 串行流
    Optional<Integer> first1 = Stream.of(1, 2, 3)
            .findFirst();

    Optional<Integer> any1 = Stream.of(1, 2, 3)
            .findAny();
    // 并行流
    Optional<Integer> first2 = Stream.of(1, 2, 3)
            .parallel()
            .findFirst();

    Optional<Integer> any2 = Stream.of(1, 2, 3)
            .parallel()
            .findAny();

    System.out.println(first1);
    System.out.println(first2);
    System.out.println(any1);
    System.out.println(any2);
}

一下是两次的执行结果。可以看到并行流两次 findAny 的结果不同。

Optional[1]
Optional[1]
Optional[1]
Optional[1]

Optional[1]
Optional[1]
Optional[1]
Optional[2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值