Java8新特性之Stream流(六)

发布时间 2023-10-12 18:44:05作者: 苦逼vs猴子

1. Stream流介绍

一种支持顺序和并行聚合操作的元素序列,能够对集合、数组进行过滤、排序、去重等操作;

Stream流与日常IO流是不同的技术体系;

官方定义:

A sequence of elements supporting sequential and parallel aggregate operations.

Stream的两种操作:

public class StreamTest {

    public static void main(String[] args) {
        Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon");
        // 中间操作
        Stream<String> namesOfStartsWithJ = names.filter(item -> item.startsWith("j"));
        // 终止操作
        namesOfStartsWithJ.forEach(System.out::println);
    }
}

// 运行结果
jerry
jack
jon

2. Stream的创建方式

  • 数组
public class StreamTest {

    public static void main(String[] args) {
        String[] names = {"tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon"};
        // Arrays工具类的静态方法stream()
        Stream<String> namesOfStream = Arrays.stream(names);
    }
}

  • 集合
public class StreamTest {

    public static void main(String[] args) {
        String[] names = {"tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon"};
        List<String> nameList = Arrays.asList(names);
        // Collection接口的默认方法stream()和parallelStream()
        Stream<String> namesOfStream1 = nameList.stream(); // 顺序流
        Stream<String> namesOfStream2 = nameList.parallelStream(); // 并行流
    }
}

  • Stream接口的静态方法
public class StreamTest {

    public static void main(String[] args) {
        // Stream接口的静态方法of()
        Stream<String> namesOfStream1 = Stream.of("tom");
        Stream<String> namesOfStream2 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon");
        // Stream接口的静态方法iterate()和generate()生成的无限流
        Stream<Integer> namesOfStream3 = Stream.iterate(1, item -> item + 1); // 迭代方式产生
        Stream<Integer> namesOfStream4 = Stream.generate(() -> new Random().nextInt(100)); // 生成方式产生
        // Stream接口的静态方法empty()生成的空流
        Stream<Integer> namesOfStream5 = Stream.empty();
    }
}

3. Stream的中间操作

  • 过滤
    • filter:对元素进行过滤;
    • distinct:对元素进行去重,必须重写元素的hashCode()和equals()方法;
public class StreamTest {

    public static void main(String[] args) {
        Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
        // 过滤不是'j'开头的名称
        names.filter(item -> item.startsWith("j"))
            // 去掉重复的名称
            .distinct()
            .forEach(System.out::println);
    }
}

// 运行结果
jerry
jack
jon
  • 切片
    • limit:截取前面n个元素;
    • skip:跳过前面n个元素,当流中元素个数不足n个时,将返回一个空流;
public class StreamTest {

    public static void main(String[] args) {
        Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
        // 截取前面3个元素
        names.limit(3)
            // 跳过前面2个元素
            .skip(2)
            .forEach(System.out::println);
    }
}

// 运行结果
jack

  • 映射
    • map:对元素进行操作,转换为另一种类型;
    • flatMap:对元素进行操作,转换为一种Stream流;
public class StreamTest {

    public static void main(String[] args) {
        Stream<String> names = Stream.of("tom", "jerry", "jack");
        // 将string名称转换为User对象
        names.map(User::new).forEach(System.out::println);
    }
}

class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User: {name=" + name + "}";
    }
}

// 运行结果
User: {name=tom}
User: {name=jerry}
User: {name=jack}

public class StreamTest {

    public static void main(String[] args) {
        Integer[] arr1 = {1, 2, 3};
        Integer[] arr2 = {4, 5, 6};
        Integer[] arr3 = {7, 8, 9};
        Stream<Integer[]> stream = Stream.of(arr1, arr2, arr3);
        // 将二维数组流转换为一维数组流
        stream.flatMap(Arrays::stream).forEach(System.out::println);
    }
}

// 运行结果
1
2
3
4
5
6
7
8
9
  • 排序
    • sorted:若元素实现了Comparable接口,重写了compareTo方法,则采用元素内部的方法比较,否则需要传入自定义的比较函数;
public class StreamTest {

    public static void main(String[] args) {
        Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
        // 将名称进行排序
        names.sorted().forEach(System.out::println);
    }
}

// 运行结果
bob
jack
jack
jerry
jon
kevin
lisa
mark
tom

4. Stream的终止操作

  • 查找
    • findFirst:返回第一个元素,注意不是直接返回的元素对象,而是被Optional容器包装的元素对象;
    • findAny:返回任意一个元素,注意也是返回的被Optional容器包装的元素对象;
    • max:返回元素中的最大值,需要传入比较函数,注意也是返回的被Optional容器包装的元素对象;
    • min:返回元素中的最小值,需要传入比较函数,注意也是返回的被Optional容器包装的元素对象;
public class StreamTest {

    public static void main(String[] args) {
        Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6);
        Optional<Integer> first = stream1.findFirst();
        System.out.println("first: " + first.get());

        Stream<Integer> stream2 = Stream.of(1, 2, 3, 4, 5, 6);
        Optional<Integer> any = stream2.findAny();
        System.out.println("any: " + any.get());

        Stream<Integer> stream3 = Stream.of(1, 2, 3, 4, 5, 6);
        Optional<Integer> max = stream3.max(Integer::compareTo);
        System.out.println("max: " + max.get());

        Stream<Integer> stream4 = Stream.of(1, 2, 3, 4, 5, 6);
        Optional<Integer> min = stream4.min(Integer::compareTo);
        System.out.println("min: " + min.get());
    }
}

// 运行结果
first: 1
any: 1
max: 6
min: 1

  • 匹配

    • allMatch:是否所有元素都匹配;

    • anyMatch:是否有任意一个元素匹配;

    • noneMatch:是否所有元素都不匹配;

public class StreamTest {

    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
        // 是否所有名称长度都小于6
        boolean result1 = stream1.allMatch(item -> item.length() < 6);
        System.out.println("result1: " + result1);

        Stream<String> stream2 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
        // 是否有名称包含字符'z'
        boolean result2 = stream2.anyMatch(item -> item.contains("z"));
        System.out.println("result2: " + result2);

        Stream<String> stream3 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
        // 是否所有名称都不是'b'开头
        boolean result3 = stream3.noneMatch(item -> item.startsWith("b"));
        System.out.println("result3: " + result3);
    }
}

// 运行结果
result1: true
result2: false
result3: false

  • 统计
    • count:统计流中元素个数;
public class StreamTest {

    public static void main(String[] args) {
        Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
        // 统计以'j'开头的名称数量
        long size = names.filter(item -> item.startsWith("j")).count();
        System.out.println("size: " + size);
    }
}

// 运行结果
size: 4

  • 遍历

    • forEach:遍历元素
public class StreamTest {

    public static void main(String[] args) {
        Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob", "kevin", "jon", "jack");
        // 遍历以'j'开头的名称
        names.filter(item -> item.startsWith("j")).forEach(System.out::println);
    }
}

// 运行结果
jerry
jack
jon
jack

  • 聚合
    • reduce:按照自定义规则对元素进行聚合操作,并返回期待的结果;
public class StreamTest {

    public static void main(String[] args) {
        Stream<Integer> stream1 = Stream.iterate(1, item -> item + 1);
        // 1~100的和
        Optional<Integer> sum1 = stream1.limit(100).reduce(Integer::sum);
        System.out.println("sum1: " + sum1.get());

        Stream<Integer> stream2 = Stream.iterate(1, item -> item + 1);
        // 初始值为100, 再加上1~100的和
        Integer sum2 = stream2.limit(100).reduce(100, Integer::sum);
        System.out.println("sum2: " + sum2);
    }
}

// 运行结果
sum1: 5050
sum2: 5150

  • 收集
    • collect:将已操作过的元素收集起来,可返回List、Set、Map等等;
public class StreamTest {

  public static void main(String[] args) {
        Stream<Integer> stream1 = Stream.iterate(1, item -> item + 1);
        // 将1~10的偶数收集到List中
        List<Integer> evenNumbers = stream1.limit(10)
            .filter(item -> item % 2 == 0)
          .collect(Collectors.toList());
        System.out.println("偶数: " + evenNumbers);

        Stream<Integer> stream2 = Stream.iterate(1, item -> item + 1);
        // 将1~10的奇偶数分类放入map中
        Map<String, List<Integer>> numberMap = stream2.limit(10)
            .collect(
                Collectors.toMap(
                    item -> item % 2 == 0 ? "偶数" : "奇数",
                    item -> {
                        List<Integer> list = new ArrayList<>();
                        list.add(item);
                        return list;
                    },
                    (oldList, newList) -> {
                        newList.addAll(oldList);
                        return newList;
                    }
                )
            );
        System.out.println("奇偶数分类: " + numberMap);
    }
}

// 运行结果
偶数: [2, 4, 6, 8, 10]
奇偶数分类: {偶数=[10, 8, 6, 4, 2], 奇数=[9, 7, 5, 3, 1]}

5. Stream的并行流

  1. Stream流可以通过实例的parallel和sequential两个方法在并行流和顺序流之间切换;
  2. Stream并行流是以Java7引入的fork/join框架为基础,以递归方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果;
public class StreamTest {

    public static void main(String[] args) {
        Stream<String> names = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob");
        names.parallel().filter(item -> {
            System.out.println("filter: " + "thread=" + Thread.currentThread().getName() + ", item=" + item);
            return item.startsWith("j");
        }).forEach(item -> System.out.println("result: " + item));
    }
}

// 运行结果
filter: thread=main, item=lisa
filter: thread=ForkJoinPool.commonPool-worker-2, item=bob
filter: thread=main, item=tom
filter: thread=ForkJoinPool.commonPool-worker-3, item=jack
filter: thread=ForkJoinPool.commonPool-worker-1, item=jerry
filter: thread=ForkJoinPool.commonPool-worker-2, item=mark
result: jerry
result: jack

6. Stream的注意事项

  1. Stream自己不会存储元素;
  2. Stream的中间操作不会改变源对象,相反它会返回一个持有结果的新Stream实例;
public class StreamTest {

    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob");
        Stream<String> stream2 = stream1.filter(item -> item.startsWith("j"));
        // stream1与stream2是两个不同的对象
        System.out.println(stream1);
        System.out.println(stream2);
    }
}

// 运行结果
java.util.stream.ReferencePipeline$Head@404b9385
java.util.stream.ReferencePipeline$2@6d311334

  1. Stream的中间操作是延迟加载的,只有当终止操作存在的时候,中间操作才会执行;
public class StreamTest {

    public static void main(String[] args) {
        Stream<String> stream1 = Stream.of("tom", "jerry", "jack", "lisa", "mark", "bob");
        Stream<String> stream2 = stream1.filter(item -> {
            // 这里的打印不会执行
            System.out.println("filter: " + item);
            return item.startsWith("j");
        });
    }
}

  1. Stream的执行实际上是每一个元素沿着执行链垂直移动的,也就是说当一个元素将执行链执行完成后才会开始第二个元素,但注意排序操作例外;
public class StreamTest {

    public static void main(String[] args) {
        Stream<Integer> stream1 = Stream.of(3, 1, 2, 5, 6, 4);
        stream1.sorted((a, b) -> {
            // 全部元素完成排序
            System.out.println("sort: " + a + ", " + b);
            return Integer.compare(a, b);
        }).filter(item -> {
            // 1. 对元素过滤
            System.out.println("filter: " + item);
            return item <= 3;
        }).map(item -> {
            // 2. 对元素操作
            System.out.println("map: " + item * 2);
            return item * 2;
        }).forEach(item -> {
            // 3. 对元素打印
            System.out.println("print: " + item);
        });
    }
}

// 运行结果
sort: 1, 3
sort: 2, 1
sort: 2, 3
sort: 2, 1
sort: 5, 2
sort: 5, 3
sort: 6, 3
sort: 6, 5
sort: 4, 3
sort: 4, 6
sort: 4, 5
filter: 1
map: 2
print: 2
filter: 2
map: 4
print: 4
filter: 3
map: 6
print: 6
filter: 4
filter: 5
filter: 6

  1. Stream的实例在执行终止操作后不能再执行其他操作,否则会抛出stream has already been operated upon or closed异常;

————————————————

原文链接:https://blog.csdn.net/gu19930914/article/details/115769134