Java8新特性之Stream流(六)


写在最前面,阅读本文需要对 lambda表达式方法引用相对熟悉;

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
    
  3. 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");
            });
        }
    }
    
  4. 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
    
  5. Stream的实例在执行终止操作后不能再执行其他操作,否则会抛出stream has already been operated upon or closed异常;


7. 相关链接

Java8新特性之Lambda表达式(一)

Java8新特性之函数式接口(二)

Java8新特性之方法引用(三)

Java8新特性之接口的默认方法和静态方法(四)

Java8新特性之重复注解和类型注解(五)

Java8新特性之Stream流(六)

Java8新特性之Optional容器(七)

Java8新特性之新的日期时间类(八)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JackieGGu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值