Java8 Stream API详细操作

一、Stream流的基本概念

1、什么是Stream流?

流是Java8引入的全新概念,它用来处理集合中的数据,对集合(Collection)对象功能的增强,专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。

Java中的Stream并不会存储元素,而是按需计算,且只能遍历一次。同时结合 Lambda 表达式,极大的提高编程效率和程序可读性。

Stream流采用内部迭代方式,通过访问者模式(Visitor)实现。

若要对集合进行处理,则需我们手写处理代码,这就叫做外部迭代。比如:使用Iterator或for循环来遍历集合。

若要对流进行处理,我们只需告诉流我们需要什么结果,处理过程由流自行完成,这就称为内部迭代。比如:Collection.forEach(…)方法迭代。

2、Stream流的操作种类

流的操作可以分为两种:中间操作和终端操作。

1. 中间操作

当数据源中的数据上了流水线后,这个过程对数据进行的所有操作都称为中间(intermediate)操作。 

中间操作可以连接起来,将一个流转换为另一个流。这样多个操作可以串联成一个管道(Pipelining), 如同流式风格(fluent style)。

中间操作都会返回流对象本身。

2. 终端操作 

当所有的中间操作完成后,若要将数据从流水线上拿下来,则需要执行终端(Terminal )操作。 

一个流只能有一个终端操作,当这个操作执行后,流的操作就结束了,无法再被操作。

Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果。

3、Stream流的操作过程

Stream流操作可以分三个步骤:

1、获取一个数据源:可以是集合,数组,I/O channel, 产生器generator 等。

2、中间操作(intermediate operation):

      在管道的节点上进行处理,比如: filter、 distinct、 sorted等,它们可以串连起来形成流水线。

3、最终操作( terminal operation):执行终端操作后本次流结束,并返回一个执行结果。

      注意:流进行了终止操作后,不能再次使用,否则会报错。

二、获取Stream流对象

在使用流之前,需要先拥有一个数据源,并通过 StreamAPI提供的一些方法获取该数据源的 Stearm接口类型流对象。

注意:

  • 除了Stream类型外,还有几个IntStream、LongStream、DoubleStream很常用, 因为boxing 和 unboxing 会很耗时,所以特别为这三种基本数值型提供了对应的 Stream。
  • 此外还有一个流类型:并行流parallelStream,parallelStream提供了流的并行处理,它是Stream的另一重要特性,其底层使用Fork/Join框架实现。

串行流:适合存在线程安全问题、阻塞任务、重量级任务,以及需要使用同一事务的逻辑。

Stearm类型有:Stream,还有IntStream、LongStream、DoubleStream也很常用,因 boxing和 unboxing会很耗时,所以,这三种基本数值型提供了对应的 Stream。

并行流:流的并行处理,适合没有线程安全问题、较单纯的数据处理任务。

Stearm类型有:并行流parallelStream

其中 parallelStream()方法能够充分利用多核 CPU的优势,使用多线程加快对集合数据的处理速度。

下面来认识常用的数据源来获取 Stearm对象。后面进行操作的更多方法等,请查看API。

public class User {
    private int id;
    private String name;
    private String sex;
    private int age;
    private int height;
...
}

1、集合 

集合 数据源比较为常用,通过 stream()方法即可获取 Stearm对象。

        List<User> list = new ArrayList<>();
        list.add(new User(1, "赵云", "男", 18, 178));
        list.add(new User(2, "后裔", "男", 20, 188));
        list.add(new User(1, "妲己", "女", 17, 175));

        Stream<User> stream = list.stream();
        Stream<User> parallelStream = list.parallelStream();

2、数组 

通过 Arrays 类提供的静态 stream()方法获取数组的 Stearm对象。

        String[] arr = new String[]{"赵云", "后裔", "妲己"};
        Stream<String> stream = Arrays.stream(arr);

3、值 

直接将几个值变成 Stearm对象,IntStream、LongStream、DoubleStream。

        IntStream intStream = IntStream.of(new int[]{1, 2, 3});
        LongStream longStream = LongStream.range(1, 3); // [1,3)
        LongStream longStream2 = LongStream.rangeClosed(1, 3); // [1,3]
        DoubleStream doubleStream = DoubleStream.of(1.0, 3.0); 

4、其他创建方法

    • static <T> Stream<T>generate(Supplier<T> s)

      返回一个无穷序列无序流,其中每个元素是由提供 Supplier生成。

      static <T> Stream<T>iterate(T seed, UnaryOperator<T> f)

      返回一个无穷序列有序 Stream由最初的一元 seed函数的 f迭代应用产生的,产生一个由 seedf(seed)f(f(seed)) Stream,等。

Stream中的 generate() 很好理解。

iterate () 方法,参数为一个种子值,和一个一元操作符(例如 f)。然后种子值成为 Stream 的第一个元素,f(seed) 为第二个,f(f(seed)) 第三个,以此类推。

注意:这两种方法创建的流是无限的,在管道中,必须利用 limit 之类的操作限制 Stream 大小。

        // 生成 10 个随机整数
        Stream<Integer> stream = Stream.generate(new Random()::nextInt).limit(10);
        // 生成10个等差(2)数列
        Stream<Integer> stream1 = Stream.iterate(0, n -> n + 2).limit(10);

三、中间操作

1、filter方法:过滤掉不符合谓词判断的数据

    • Stream<T>filter(Predicate<? super T> predicate)

      返回由该流的元素组成的流,该元素与给定的谓词匹配。

在执行过程中,流将元素逐一输送给 filter,并筛选出 Predicate函数执行结果为 true的元素。 

    public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        initList(list);
        List<User> userList = list.stream().filter(u -> "男".equals(u.getSex())).collect(Collectors.toList());
        userList.forEach(System.out::println);
    }

    private static void initList(List<User> list) {
        list.add(new User(1, "赵云", "男", 18, 178));
        list.add(new User(2, "后裔", "男", 20, 188));
        list.add(new User(1, "妲己", "女", 17, 175));
    }

2、distinct方法:去重

        long count = IntStream.of(1, 2, 3, 4, 3, 2, 1).distinct().count();
        System.out.println(count); // 4

3、limit方法:截取多少条数据 

    • Stream<T>limit(long maxSize)

      返回一个包含该流的元素流,截断长度不超过 maxSize

4、skip方法:跳过前几条数据 

    • Stream<T>skip(long n)

      返回一个包含此流的其余部分丢弃的流的第一 n元素后流。

        List<User> userList = list.stream().filter(u -> "男".equals(u.getSex())).skip(1).collect(Collectors.toList());
        userList.forEach(System.out::println); // 后裔

5、map方法:映射,将一个元素转换成另一个元素类型

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

      返回一个流,包括将给定函数应用到该流元素的结果。

对流中的每个元素执行一个函数,使得元素转换成另一种类型输出。流会将每一个元素输送给map函数,并执行map中的 Lambda表达式,最后将执行结果存入一个新的流中。 

List<String> nameList = list.stream().map(User::getName).collect(Collectors.toList());
        nameList.forEach(System.out::println);

6、flatMap方法:合并多个流

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

      返回由将所提供的映射函数应用到每个元素的映射流的内容替换此流的每个元素的结果的结果流。

注意:

  • map 方法:是将元素转换成另一种类型输出,返回一个大流,大流里面包含了一个个小流,
  • flatMap 方法:是将大流中的这些小流合并成一个流返回。
        List<String> list = new ArrayList<String>();
        list.add("a,b,c,d");
        list.add("e,f,a,b");
        list.add("h,i,c,d");
        List<String[]> list1 = list.stream().map(line -> line.split(",")).collect(Collectors.toList());
        List<String> list21 = list.stream().map(line -> line.split(",")).flatMap((arr) -> Arrays.stream(arr)).collect(Collectors.toList());
        List<String> list22 = list.stream().map(line -> line.split(",")).flatMap(Arrays::stream).distinct().collect(Collectors.toList());

     

7、peek方法:遍历操作

    • Stream<T>peek(Consumer<? super T> action)

      返回由该流的元素组成的流,并在所提供的流中执行所提供的每个元素上的动作。

peek()操作,当元素被消费时(终端操作方法被调用时),会触发前面 peek。有多少个就触发多少次。

        // 不会有任何的输出
        Stream.of("a", "b", "c", "d").peek(e -> System.out.print(e + " "));
        // 会输出
        Stream.of("a", "b", "c", "d").peek(System.out::print).collect(Collectors.toSet());

8、unordered方法:返回一个无序的流,对于不关心顺序的数据处理和并行配合使用更佳。 

        IntStream.rangeClosed(0,9).unordered().forEach(System.out::print); // 0123456789
        System.out.println();
        IntStream.rangeClosed(0,9).parallel().unordered().forEach(System.out::print); // 6570918342

如果不关心顺序,则可以使用并行流,这样使用,能提高 CPU的利用率,进而提高处理的效率。比如。某活动(参与用户大数量)随机获取三名幸运观众。

9、sorted方法:对数据排序

    • Stream<T>sorted()

      返回由该流的元素组成的流,按自然顺序排序。

      Stream<T>sorted(Comparator<? super T> comparator)

      返回一个包含该流的元素流,根据提供的 Comparator排序。

      应用非常广泛,重点掌握。

// 年龄升序
List<User> userList = list.stream().sorted(Comparator.comparingInt(User::getAge)).collect(Collectors.toList());
userList.forEach(System.out::println);

四、终端操作

1、allMatch方法:匹配所有元素都满足返回true

2、anyMatch方法:匹配任何元素有满足返回true

3、noneMatch方法:匹配所有元素都不满足返回true

noneMatch与 allMatch恰恰相反。

booleanallMatch(Predicate<? super T> predicate)

返回此流中的所有元素是否匹配所提供的谓词。

booleananyMatch(Predicate<? super T> predicate)

返回此流中的任何元素是否匹配所提供的谓词。

booleannoneMatch(Predicate<? super T> predicate)

返回此流中的任何元素是否匹配所提供的谓词。

    public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        initList(list);
        // 判断所有人是否都为男性
        boolean b1 = list.stream().allMatch(user -> "男".equals(user.getSex()));
        System.out.println(b1); // false
        // 判断所有人中是否有个男性
        boolean b2 = list.stream().anyMatch(user -> "男".equals(user.getSex()));
        System.out.println(b2); // true
        // 判断所有人中是否都为女性
        boolean b3 = list.stream().noneMatch(user -> "男".equals(user.getSex()));
        System.out.println(b3); // false
    }

    private static void initList(List<User> list) {
        list.add(new User(1, "赵云", "男", 18, 178));
        list.add(new User(2, "后裔", "男", 20, 188));
        list.add(new User(1, "妲己", "女", 17, 175));
    }

4、findAny方法:获取任一元素。

findAny能够从流中随便选一个元素出来(该操作行为明确不确定的),它返回一个 Optional类型的元素。比如:随机抽取一名幸运观众。

5、findFirst方法:获取第一个元素

        OptionalInt optional = IntStream.rangeClosed(1, 1000).findFirst();
        System.out.println(optional.getAsInt());

        OptionalInt optionalInt = IntStream.rangeClosed(1, 1000).parallel().findAny();
        System.out.println(optionalInt.getAsInt());

6、forEach和 forEachOrdered方法:遍历每一个元素

    • voidforEach(Consumer<? super T> action)

      对该流的每个元素执行一个动作。

      voidforEachOrdered(Consumer<? super T> action)

      对该流的每个元素执行一个操作,如果流有一个定义的遇到顺序,则在该流的遇到顺序中执行一个动作。

        IntStream.rangeClosed(0,9).parallel().unordered().forEachOrdered(System.out::print); // 0123456789

7、mapToXxx方法:将普通流转换成数值流

提供了将普通流转换成数值流的三种方法:mapToInt、mapToDouble、mapToLong。

IntStream intStream = list.stream().mapToInt(User::getAge);

 8、数值计算

每种数值流都提供了数值计算函数,如max、min、sum等。 

        OptionalDouble average = list.stream().mapToInt(User::getAge).average();
        System.out.println(average.getAsDouble()); // 18.333333333333332

 9、reduce方法:能实现归约,输出一个值(任何类型)。

归约是将集合中的所有元素经过指定运算,折叠成一个元素值输出。比如:求最值、平均数等。

三个参数的含义:

identity:一个初始化的值;这个初始化的值其类型是泛型U(与Stream中元素的类型可以不一样也可以一样),与Reduce方法返回的类型一致;

accumulator:其类型是 BiFunction函数(输入U与T两个类型的参数数据,而返回计算后的数据)

combiner: 其类型是 BinaryOperator函数,支持的是对U类型的对象进行操作,主要是使用在并行计算的场景下。注意:如果Stream是非并行时,第三个参数不起作用。

        ArrayList<String> result = Stream.of("a", "b", "c", "d").reduce(new ArrayList<>(),
                (u, s) -> {
                    u.add(s);
                    return u;
                }, (strings, strings2) -> strings);   //无效的
        System.out.println(result); //[a, b, c, d]

        Integer reduce = Stream.of(1, 2, 3).reduce(4,
                (integer, integer2) -> integer + integer2,
                (integer, integer2) -> integer * integer2); //无效的
        System.out.println(reduce); //10

        Integer reduce2 = Stream.of(1, 2, 3).parallel().reduce(4,
                (integer, integer2) -> integer + integer2,  //开启三个线程,每个线程里对应一个元素+4
                (integer, integer2) -> integer * integer2); //有效的,它会将不同线程计算的结果调用combiner做汇总后返回。
        System.out.println(reduce2); //210

        int accResult = Stream.of(1, 2, 3)
                .reduce(4, (acc, item) -> {
                    System.out.println("acc : "  + acc);
                    acc += item;
                    System.out.println("item: " + item);
                    System.out.println("acc+ : "  + acc);
                    System.out.println("--------");
                    return acc;
                });
        System.out.println("accResult: " + accResult); //10

    

也可以进行元素求最值、平均数等操作。

        int sum1 = 0;
        for (User user : list) {
            sum1 = sum1 + user.getAge();
        }
        System.out.println(sum1); // 55

        Optional<Integer> sum = list.stream().map(user -> user.getAge()).reduce((u1, u2) -> u1 + u2);
        System.out.println(sum.get()); // 55

10、collect方法:收集方法,对于 reduce和 collect方法个人更喜欢 collect的强大。

collect(Collector c)  将流转换为其他形式,接收一个 Collector接口的实现,用于给Stream中元素做汇总处理。

下面汇总的方法来自:JAVA8之collect总结

工厂方法返回类型作用
toListList<T>把流中所有元素收集到List中
                          示例:List<Menu> menus = Menu.getMenus.stream().collect(Collectors.toList())
toSetSet<T>把流中所有元素收集到Set中,并删除重复项
                          示例:Set<Menu> menus = Menu.getMenus.stream().collect(Collectors.toSet())
toCollectionCollection<T>把流中所有元素收集到创建的集合中
                          示例:ArrayList<Menu> menus = Menu.getMenus.stream().collect(Collectors.toCollection(ArrayList::new))
CountingLong计算流中元素的个数
                          示例:Long count = Menu.getMenus.stream().collect(counting);
SummingIntInteger对流中元素的一个整数属性求和
                          示例:Integer count = Menu.getMenus.stream().collect(summingInt(Menu::getCalories))
averagingIntDouble计算流中元素Integer属性的平均值
                          示例:Double averaging = Menu.getMenus.stream().collect(averagingInt(Menu::getCalories))
summarizingIntIntSummaryStatistics收集流中元素Integer属性的统计值,如:平均值
                          示例:IntSummaryStatistics summary = list.stream().collect(Collectors.summarizingInt(Menu::getCalories));
JoiningString连接流中每个元素的字符串
                          示例:String name = Menu.getMenus.stream().map(Menu::getName).collect(joining(“, ”))
maxByOptional<T>根据比较器选出的最大值的optional
如果为空返回的是Optional.empty()
                          示例:Optional<Menu> fattest = Menu.getMenus.stream().collect(maxBy(Menu::getCalories))
minByOptional<T>根据比较器选出的最小值的的optional
如果为空返回的是Optional.empty()
                         示例: Optional<Menu> lessest = Menu.getMenus.stream().collect(minBy(Menu::getCalories))
Reducing归约操作产生的类型从一个作为累加器的初始值开始,利用binaryOperator与流中的元素逐个结合,从而将流归约为单个值
                         示例:int count = Menu.getMenus.stream().collect(reducing(0,Menu::getCalories,Integer::sum));
collectingAndThen转换函数返回的类型包裹另一个转换器,对其结果应用转换函数
                                示例:Int count = Menu.getMenus.stream().collect(collectingAndThen(toList(),List::size))
groupingByMap<K,List<T>>根据流中元素的某个值对流中的元素进行分组,属性值为K, 结果为V
                          示例:Map<Type,List<Menu>> menuType=Menu.getMenus.stream().collect(groupingby(Menu::getType))
partitioningByMap<Boolean,List<T>>根据流中每个元素应用谓语的结果(true/false)进行分区
                          示例:Map<Boolean,List<Menu>> menuType=Menu.getMenus.stream().collect(partitioningBy(Menu::isType));

这里使用几个方法,具体请查看 Collectors API。

1. toMap方法: 

static <T,K,U> Collector<T,?,Map<K,U>>toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)

返回一个 Collector积累成一个 Map元素的键和值是应用提供的函数映射到输入元素的结果。

static <T,K,U> Collector<T,?,Map<K,U>>toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)

返回一个 Collector积累成一个 Map元素的键和值是应用提供的函数映射到输入元素的结果。

static <T,K,U,M extends Map<K,U>>
Collector<T,?,M>
toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)

返回一个 Collector积累成一个 Map元素的键和值是应用提供的函数映射到输入元素的结果。

如果需求使用线程安全的Map,可以用 toConcurrentMap、groupingByConcurrent。

使用两个参数

注意:

  • 当 key重复时,会抛出异常:java.lang.IllegalStateException: Duplicate key *
  • 当 value为 null 时,会抛出异常:java.lang.NullPointerException
    public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        initList(list);

        Map<String, Integer> map= list.stream().collect(Collectors.toMap(User::getName, User::getAge));
        System.out.println(map); // {妲己=18, 貂蝉=18, 露娜=20, 后裔=20, 赵云=18, 鲁班=9}
    }

    private static void initList(List<User> list) {
        list.add(new User(1, "赵云", "男", 18, 178));
        list.add(new User(2, "后裔", "男", 20, 190));
        list.add(new User(6, "妲己", "女", 18, 175));
        list.add(new User(3, "鲁班", "男", 9, 155));
        list.add(new User(8, "貂蝉", "女", 18, 175));
        list.add(new User(9, "露娜", "女", 20, 175));
    }

使用三个参数

当两个 key相同时,只能有一个key存在,那对应的 value如何处理?  value交由我们自己处理。

// key重复,v覆盖
Map<String, User> collect = list.stream().collect(Collectors.toMap(User::getSex, user -> user, (oldUser, newUser)->newUser));
System.out.println(collect);
// {女=User{id=1, name='露娜', sex='女', age=20, height=175}, 男=User{id=1, name='鲁班', sex='男', age=9, height=155}}

使用四个参数

mergeFunction和 mapSupplier 调用者可以自定义希望返回什么类型的 Map。

        // List转成Map,且保证List的顺序,使用LinedHashMap。
        LinkedHashMap<Integer, User> linkedHashMap = list.stream()
                .sorted(Comparator.comparingInt(User::getId))
                .collect(Collectors.toMap(User::getId, user -> user, (oldUser, newUser) -> newUser, LinkedHashMap::new));
        linkedHashMap.forEach((k, v) -> {
            System.out.println(k + "==" + v);
        });

      

2. mapping 方法:

static <T,U,A,R> Collector<T,?,R>mapping(Function<? super T,? extends U> mapper, Collector<? super U,A,R> downstream)

适应 Collector接受型 U元素之一接受元素类型 T应用映射功能,每个输入元素之前的积累。

        // 和中间操作map方法类似
        String collect = list.stream().collect(Collectors.mapping(u -> {
            return new Float(u.getHeight() / 100 + "." + u.getHeight() % 100).toString();
        }, Collectors.joining(",")));
        System.out.println(collect); // 1.78,1.9,1.75,1.55,1.75,1.75

3. maxBy、minBy方法:

static <T> Collector<T,?,Optional<T>>maxBy(Comparator<? super T> comparator)

返回一个 Collector产生极大元根据给定的 Comparator,描述为一个 Optional<T>

static <T> Collector<T,?,Optional<T>>minBy(Comparator<? super T> comparator)

返回一个 Collector产生最小的元素按照一定的 Comparator,描述为一个 Optional<T>

static <T,A,R,RR> Collector<T,A,RR>collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher)

适应 Collector执行一个额外的加工转化。

当使用 maxBy、minBy统计最值时,结果会封装在 Optional中。

如果我们明确结果不可能为null时,可以采用 collectingAndThen函数包裹 maxBy、minBy,从而将Optional对象进行转换值。

        // age最大
        Optional<User> optional = list.stream().collect(Collectors.maxBy((u1, u2) -> {
            return u1.getAge() - u2.getAge();
        }));
        System.out.println(optional);

        // age最小
        User user = list.stream().collect(Collectors.collectingAndThen(Collectors.minBy((u1, u2) -> {
            return u1.getAge() - u2.getAge();
        }), Optional::get));
        System.out.println(user);

    

4. groupBy和 groupingByConcurrent方法:

    API有几个重载的方法,对数据分组,并且可以无限的分组下去。

    注意:

  • groupBy返回的 Map,
  • groupingByConcurrent 返回的是 ConcurrentMap。

单字段分组

        // 根据性别分组
        ConcurrentMap<String, List<User>> concurrentMap = list.stream()
                .collect(Collectors.groupingByConcurrent(User::getSex));
        concurrentMap.forEach((k, v) -> {
            System.out.println(k + "==" + v);
        });

 多字段分组

        // 根据性别,年龄分组
        Map<String, Map<Integer, List<User>>> map = list.stream()
                .collect(Collectors.groupingBy(User::getSex, Collectors.groupingBy(User::getAge)));
        map.forEach((k, m) -> {
            m.forEach((k2, v) -> {                
                System.out.println(k + "==" + k2 + "==" + v);
            });
        });

单字段分组并统计

        // 根据性别分组并统计个数
        Map<String, Long> map = list.stream()
                .collect(Collectors.groupingBy(User::getSex, Collectors.counting()));
        map.forEach((k, v) -> {
            System.out.println(k + "==" + v);
        });
==
女==3
男==3

5. partitioningBy方法:分区

static <T> Collector<T,?,Map<Boolean,List<T>>>partitioningBy(Predicate<? super T> predicate)

返回一个 Collector分区根据 Predicate输入元素,并将它们组织成一个 Map<Boolean, List<T>>

static <T,D,A> Collector<T,?,Map<Boolean,D>>partitioningBy(Predicate<? super T> predicate, Collector<? super T,A,D> downstream)

返回一个 Collector分区的输入元素,根据 Predicate,降低值在每个分区根据另一 Collector,并将它们组织成一个 Map<Boolean, D>其值是下游减少的结果。

  分区是分组的一种特殊情况,它只能分成true、false两组。

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 5, 5, 6, 9);

        Map<Boolean, List<Integer>> partition = list.stream().collect(Collectors.partitioningBy(x -> x >= 4));
        System.out.println(partition); 

        Map<Boolean, Map<Boolean, List<Integer>>> partition2 = list.stream()
                .collect(Collectors.partitioningBy(x -> x >= 4, Collectors.partitioningBy(x -> x > 6)));
        System.out.println(partition2); 

        //也可以结合groupBy等
        Map<Boolean, Map<Integer, List<Integer>>> collect = list.stream()
                .collect(Collectors.partitioningBy(x -> x >= 4, Collectors.groupingBy(x -> x)));
        System.out.println(collect); 

   

6. joining方法

joining方法可以用来连接,这个应用 在比如字符串拼接的场景。

        List<String> list = Arrays.asList( "a","b","1","c","d");
        String joinStr = list.stream().collect(Collectors.joining(":"));
        System.out.println(joinStr); // a:b:1:c:d

7. 生成统计信息的方法

static <T> Collector<T,?,DoubleSummaryStatistics>summarizingDouble(ToDoubleFunction<? super T> mapper)

返回一个 Collector采用 double-producing映射功能,每个输入元素,并返回结果值汇总统计。

static <T> Collector<T,?,IntSummaryStatistics>summarizingInt(ToIntFunction<? super T> mapper)

返回一个 Collector采用 int-producing映射功能,每个输入元素,并返回结果值汇总统计。

static <T> Collector<T,?,LongSummaryStatistics>summarizingLong(ToLongFunction<? super T> mapper)

返回一个 Collector采用 long-producing映射功能,每个输入元素,并返回结果值汇总统计。

IntSummaryStatistics、LongSummaryStatistics和 DoubleSummaryStatistics对象包含的很多统计信息。比如:最值,平均值等。

        // 统计list的age信息
        IntSummaryStatistics summary = list.stream().collect(Collectors.summarizingInt(User::getAge));
        System.out.println(summary.getMax()); // 20
        System.out.println(summary.getMin()); // 9
        System.out.println(summary.getSum()); // 103
        System.out.println(summary.getAverage()); // 17.166666666666668
        System.out.println(summary.getCount()); // 6

也可以使用 combine方法:将一个 IntSummaryStatistics与另一个 IntSummaryStatistics组合起来(必须是同一类型)。

    • voidaccept(int value)

      将一个新的值记录到汇总信息中

      voidcombine(IntSummaryStatistics other)

      结合另一个 IntSummaryStatistics状态进入这一。

        // 统计list的age信息
        IntSummaryStatistics summary = list.stream().collect(Collectors.summarizingInt(User::getAge));

        // 添加一个age
        IntSummaryStatistics summary2 = new IntSummaryStatistics();
        summary2.accept(100);
        summary.combine(summary2);

        System.out.println(summary.getMax()); // 100
        System.out.println(summary.getMin()); // 9
        System.out.println(summary.getSum()); // 203
        System.out.println(summary.getAverage()); // 29.0
        System.out.println(summary.getCount()); // 7

    Stream 结合 Lambda表达式,函数式接口等操作,使代码简洁,优雅,对数据的处理更加灵活。在开发中推荐使用。

8、并行流

一个简单 demo使用并行流:

        //List<String> list = Arrays.asList( "a","b","1","c","d");
        List<String>  list = Collections.synchronizedList(Arrays.asList( "a","b","1","c","d"));

        list.parallelStream().forEach(i -> {
            System.out.println(Thread.currentThread().getName() + " -> value:" + i);
        });

注意:打印结果可以看出 parallelStream内部到底使用了不同线程,处理数据也是无序的。

Collection接口的实现类调用 parallelStream()方法就可以实现并行流,相应地也获得了并行计算的能力。或者Stream接口的实现调用 parallel()方法也可以得到并行流。
并行流实现机制是基于 fork/join 框架,将问题分解再合并处理。 不过并行计算是否一定比串行快呢?这也不一定。

使用并行流的一些注意点:
(1)使用 parallelStream并行流一定要使用线程安全的对象。
(2)使用 parallelStream并行流也不一定会提升性能。
在CPU资源紧张的时候,使用并行流可能会带来频繁的线程上下文切换,导致并行流执行的效率还没有串行执行的效率高。

参考文章:

谈谈并行流parallelStream

Java 8系列之Stream的基本语法详解

—— Stay Hungry. Stay Foolish. 求知若饥,虚心若愚。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值