Java Stream编程

Stream编程

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
但请注意区别于我们的io流,Java中的Stream并不会存储元素,而是按需计算。

Stream 操作的两大特性

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。

外部迭代是一个串行的操作,如果数据量过大,性能可能会受影响,这样我们可能就需要自己去做线程池,做拆分。内部迭代的话可以用并行流,达到并行操作,不用开发人员去关系线程的问题。

中间操作

Stream 中调用后返回stream流的方法,都是属于中间操作。

终止操作

终止操作返回的是一个结果,就是所有中间操作做完以后进行的一个操作,比如汇总,求和,遍历输出等等操作。

惰性求值

不进行终止操作的情况下,中间操作不会执行。

Stream 编程三个步骤

  1. 获取 Stream 对象
  2. 中间操作
  3. 终止操作

获取 Stream 对象

  1. 集合(Collection、Map)丢下的 stream / parallelStream 方法获取
  2. Arrays.stream 方法获取
  3. Stream对象的静态方法获取(Stream.of / Stream.iterate / Stream.generate)
  4. IntStream、LongStream、DoubleStream(静态方法 generate / of / range / rangeClosed )
  5. Random 对象方法(ints、longs、doubles)

中间操作

方法 说明
map / mapToObj / mapToLong / mapToDouble 将流中的元素映射成另外一种元素
floatMap / flatMapToInt / flatMapToLong / flatMapToDouble 类似于笛卡尔积的映射
filter 对流中的元素进行过滤操作
peek 获取元素,但不能对元素进行修改
unordered 返回无序流
distinct 去重
sorted 排序
limit 限制个数
skip 跳过个数

map 和 floatMap

    @Test
    public void demo2(){
        List<String> list = Arrays.asList("1","2","3");

        // map 的参数Function 是 T -> R (理解为单纯的转换类型)
        list.stream().map(Long::parseLong).forEach(System.out::println);
        System.out.println("============================================");
        // flatMap 的参数Function 是 T -> Stream(理解为元素与另一个Stream对应并转换为Stream)
        list.stream().flatMap(t -> list.stream().map(k -> t + ":"+k)).forEach(System.out::println);

    }
1
2
3
============================================
11
12
13
21
22
23
31
32
33

filter 过滤元素

    @Test
    public void demo3(){
        // filter 接收一个 Predicate ,用于判断,返回满足要求的 Stream
        Stream.of(1,2,3,4,5,7,8).filter(t -> t > 5).forEach(System.out::println);
        System.out.println("============================================");
        // 比如我们将 flatMap 的结果,过滤为 元素值相对应的结果
        Stream.of(1,2,3,4,5,7,8)
                .flatMap(t -> Stream.of(1,2,3,4,5,7,8,0)
                .filter(n -> n == t)
                .map(k -> t + ":"+k))
                .forEach(System.out::println);
    }
7
8
============================================
11
22
33
44
55
77
88

peek 获取元素

    @Test
    public void demo4(){
        Stream.of(1,2,3,4,5,7,8)
                // peek 接收 Consumer,意味着它不能修改元素的值
                .peek(System.out::println)
                .map(t -> t * t)
                .peek(System.out::println)
                // peek是中间操作,不会执行,需要执行结束操作才会执行
                .forEach(System.out::println); 
    }
1
1
1
2
4
4
3
9
9
4
16
16
5
25
25
7
49
49
8
64
64

peek无法修改元素的值,那么它到底有什么作用呢,如同我们的打印结果一样,我们的第二个peek打印的是经过map后的元素的值,当我们中间操作特别多的时候,我们可以利用peek来验证得到的值是否符合我们的要求。

从打印的结果,我们还可以得到一个结论,那就是元素是一个一个的经过Stream的操作的,不是整体执行的。

unordered 无序化

    @Test
    public void demo5(){
        Stream.of(1,2,3,4,5,7,8)
                // 返回一个无序流,可能返回自身,以让后续的操作可以不考虑顺序,可提高并行执行的性能
                .unordered()
                .forEach(System.out::println);
        System.out.println("=====================");
        Stream.of(1,2,3,4,5,7,8)
                .unordered()
                .parallel()
                .forEach(System.out::println);
    }

distinct、sorted、limit、skip

    @Test
    public void demo6(){
        Stream.of(1,2,3,6,8,4,5,7,8,1,4)
                // 去重
                .distinct()
                // 排序
                .sorted()
                // 剩下的取 5条
                .limit(5)
                // 剩下的跳过 2 条
                .skip(2)
                .forEach(System.out::println);
        System.out.println("========================");
        Stream.of(1,2,3,6,8,4,5,7,8,1,4)
                // 剩下的取 5条
                .limit(5)
                // 去重
                .distinct()
                // 排序
                .sorted()
                // 剩下的跳过 2 条
                .skip(2)
                .forEach(System.out::println);

    }
3
4
5
========================
3
6
8

终止操作

方法 说明
forEach / forEachOrdered 遍历元素
toArray / collect 元素汇聚(汇聚成数组或集合)
reduce 递归计算
min / max / count / sum / average 最大/最小/元素个数/求和/求平均
findFirst / findAny 查找第一个 / 查找任意一个
anyMatch / allMatch / noneMatch 至少一个满足 / 全部满足 / 全部不满足

collect 收集器

    @Test
    public void demo8(){
        // 直接转换为 List
        List<Integer> list = Stream.of(1,8,2,7,8,3,5,3,5,2,4).collect(Collectors.toList());
        System.out.println(list);
        System.out.println("===========================");
        // 收集为 Set (可达到去重效果)
        Set<Integer> set = Stream.of(1,8,2,7,8,3,5,3,5,2,4).collect(Collectors.toSet());
        System.out.println(set);
        System.out.println("===========================");
        // 收集为 Set (可达到去重效果)
        Set<Integer> hashSet = Stream.of(1,8,2,7,8,3,5,3,5,2,4).collect(Collectors.toCollection(HashSet::new));
        System.out.println(hashSet);
        System.out.println("===========================");
    }
    @Test
    public void demo9(){
        // 本示例用于将对象列表转换为Map,使用id为Key,对象本身为value
        Map<Long,User> map = Stream.of(new User(1L,"张飞",18),
                new User(2L,"关羽",20),
                new User(3L,"刘备",22),
                new User(4L,"诸葛亮",10),
                new User(5L,"曹操",36),
                new User(6L,"孙权",12),
                new User(7L,"张辽",19))
                // Collectors.toMap 前两个参数都是Function,用于定义map的key和value
                .collect(Collectors.toMap(User::getId, user -> user));
        System.out.println(map);
    }
    @Test
    public void demo10(){
        // 本示例用于将对象列表转换为Map,使用id为Key,对象本身为value
        Map<Boolean, List<User>> map = Stream.of(new User(1L,"张飞",18),
                        new User(2L,"关羽",20),
                        new User(3L,"刘备",22),
                        new User(4L,"诸葛亮",10),
                        new User(5L,"曹操",36),
                        new User(6L,"孙权",12),
                        new User(7L,"张辽",19))
                // 年龄大于20的为一组,小于等于20的为一组 
                .collect(Collectors.partitioningBy(user -> user.getAge() > 20));
        System.out.println(map);
    }
    @Test
    public void demo11(){
        // 本示例用于将对象列表转换为Map,使用id为Key,对象本身为value
        Map<String, List<User>> map = Stream.of(new User(1L,"张飞",18,"蜀国"),
                        new User(2L,"关羽",20,"蜀国"),
                        new User(3L,"刘备",22,"蜀国"),
                        new User(4L,"诸葛亮",10,"蜀国"),
                        new User(5L,"曹操",36,"魏国"),
                        new User(6L,"孙权",12,"吴国"),
                        new User(7L,"张辽",19,"魏国"))
                // 按阵营分组 groupingBy
                .collect(Collectors.groupingBy(User::getCountry));
        System.out.println(map);
    }

reduce / min / max / count / sum / average

    @Test
    public void demo7(){
        // 返回一个 Optional
        Optional<Integer> rs = Stream.of(1,2,3,4)
                // 接收一个 BinaryOperator , BinaryOperator 继承自 BiFunction
                // BiFunction 的函数式方法为:R apply(T t, U u);类似于Function只不过它是将T和U进行运算后得到R
                .reduce(Integer::sum);

        System.out.println(rs.get());
        System.out.println("==============================");

        // 返回具体的类型
        int rs1 = Stream.of(1,2,3,4)
                // identity 表示结果的初始值 (类似于我们常规写法求和时会先定义一个 int sum = 0)
                // 接收一个 BinaryOperator , BinaryOperator 继承自 BiFunction
                // BiFunction 的函数式方法为:R apply(T t, U u);类似于Function只不过它是将T和U进行运算后得到R
                .reduce(0,Integer::sum);
        System.out.println(rs1);
        System.out.println("==============================");
        long count1 = Stream.of(1,2,3,4).count();
        long count2 = IntStream.of(1,2,3,4).count();
        System.out.println("count1:" + count1 + ";count2:" + count2);

        System.out.println("==============================");
        // 注意如下两种写法的区别(max同理,这里就不做示例了)
        Optional<Integer> min1 = Stream.of(1,2,3,4).min(Integer::compare);
        OptionalInt min2 = IntStream.of(1,2,3,4).min();
        System.out.println("min1:" + min1.get() + ";min2:" + min2.getAsInt());

        System.out.println("==============================");
        // 注意:sum和average只有数字类的Stream才有此方法
        int sum = IntStream.of(1,2,3,4).sum();
        OptionalDouble avg = IntStream.of(1,2,3,4).average();
        System.out.println("sum:" + sum + ";avg:" + avg.getAsDouble());
    }

你可能感兴趣的:(人在江湖之Java基础,java,Stream,map,floatMap,reduce)