JDK8漫谈——集合更强大

解决什么问题

  • 集合计算不足
  • 解决重复代码

背后思想

  • 管道
  • 封装
  • 数据处理

内容说明

是什么

  • 计算担当。集合用于数据存储,流用于数据计算,不会修改原始数据
  • 内置循环。高级迭代器,内置循环和计算
  • 单向。数据只能遍历一次,遍历过一次后即用尽了,像水流过,不可往复

生命周期

Stream像操作SQL一样处理数据,所以很概念非常相近,可以对比着理解 。

创建

分为静态创建和实例创建,不同的方法适用于不同场景。目的都是为了转成流

    /**
     * 初始
     * <p>
     *     适用于数组和简单的数据转为stream
     * </p>
     */
    @Test
    public void test_static_of() {
        Stream.of(Person.builder().name("name").age(1));

        /**
         * 打印的是list的元素
         */
        Stream.of(newArray()).forEach(System.out::println);
        /**
         * 打印的是list的toString,所以集合不使用该方法
         */
        Stream.of(newList()).forEach(System.out::println);
    }

    /**
     * 范围
     * <p>
     *     适用于整数迭代
     * </p>
     */
    @Test
    public void test_static_range(){
        /**
         * 不包含10
         */
        IntStream.range(1,10).forEach(System.out::println);
        /**
         * 包含10
         */
        IntStream.rangeClosed(1,10).forEach(System.out::println);
    }

    /**
     * 生成
     * <p>
     *     适用于批量生成并且赋值
     *     比如生成id或者uuid
     * </p>
     */
    @Test
    public void test_static_generate() {
        Stream.generate(UUID::randomUUID)
                .limit(10)
                .forEach(System.out::println);

        Stream.generate(()->Math.random()*1000)
                .limit(10)
                .forEach(System.out::println);

    }

    @Test
    public void test_static_iterate() {
        Stream.iterate(10, index -> {
            Person.builder().name("name").age(index);
            return --index;
        }).limit(10).forEach(System.out::println);
    }

    /**
     * 集合
     */
    @Test
    public void test_instance_collection() {
        /**
         * 打印的是list的元素
         */
        newList().stream().forEach(System.out::println);
    }

    /**
     * 正则
     */
    @Test
    public  void test_regex() {
        String sentence = "Program creek is a Java site.";
        Pattern.compile("\\W").splitAsStream(sentence).forEach(System.out::println);
    }

    /**
     * 文件
     * @throws IOException
     */
    @Test
    public void test_file() throws IOException {
        String fileName = "c://lines.txt";
        Files.lines(Paths.get(fileName)).forEach(System.out::println);
    }

    private List<String> newList() {
        List<String> list = new ArrayList<>();
        list.add("lisi");
        return list;
    }


    private String[] newArray() {
        return new String[]{"sili", "wangwu"};
    }

转换

把一个流转为另一个流,可以不断的转换。包括:过滤,转换,限制,排序,去重。通过不断的转换最终获取目标数据

    private Stream<Person> newStream(){
        List<Person> list = new ArrayList<>();
        list.add(Person.builder().name("zhangsan").age(12).build());
        list.add(Person.builder().name("zhangsan").age(12).build());
        list.add(Person.builder().name("lisi").age(18).build());
        list.add(Person.builder().name("wangwu").age(25).build());
        return list.stream();
    }

    /**
     * 过滤
     * <p>
     *     过滤不符合条件的数据。
     *     比如说:
     *     <ul>
     *         <li>结合数据库查询,在java里过滤数据</li>
     *         <li>过滤掉npe</li>
     *         <li>过滤不符合业务数据</li>
     *     </ul>
     * </p>
     */
    @Test
    public void test_filter(){
        newStream()
                .filter(person->person.getAge()>=18)
                .forEach(System.out::println);

    }

    /**
     * 类型转换
     * <p>
     *     用于快速转换数据类型
     *     比如说:
     *     <ul>
     *         <li>根据实例快速转出id集合</li>
     *     </ul>
     * </p>
     */
    @Test
    public void test_mapper(){
        newStream()
                .map(Person::getId)
                .forEach(System.out::println);
    }

    /**
     * 限制
     * <p>
     *     取前几个
     * </p>
     */
    @Test
    public void test_limit(){
        newStream()
                .limit(2)
                .forEach(System.out::println);
    }

    /**
     * 排序
     */
    @Test
    public void test_sorted(){
        newStream()
                .sorted((p1,p2)->{
                    if(p1.getAge()>p2.getAge()){
                        return 1;
                    }else if (p1.getAge()<p2.getAge()){
                        return -1;
                    }else{
                        return 0;
                    }
                })
                .forEach(System.out::println);
    }
    /**
     * 排序
     */
    @Test
    public void test_sorted_comparator() {
        newStream()
                .sorted(Comparator
                        //排序
                        .comparing(Person::getAge)
                        //倒序
                        .reversed()
                        .thenComparing(Person::getName)
                )
                .forEach(System.out::println);
    }

    /**
     * 去重
     */
    @Test
    public void test_distinct(){
        newStream()
                .distinct()
                .forEach(System.out::println);
    }

    /**
     * 综合使用
     * <p>
     *     通过这些计算可以快速找出期待的数据,而且还不需要不断的添加遍历和破坏代码可读性
     * </p>
     */
    @Test
    public void test_combine(){
        newStream()
                .filter(person->null==person)
                .distinct()
                .filter(person -> person.getAge()>=18)
                .map(Person::getAge)
                .sorted((age1,age2)->{
                    if(age1>age2){
                        return 1;
                    }else if (age1<age2){
                        return -1;
                    }else{
                        return 0;
                    }
                })
                .limit(1)
                .forEach(System.out::println);
    }
  • 无须重复循环
  • 添加需求时,无须破坏式的修改内容

聚合

一个流只能有一个中止动作。中止后流就不能使用。包括:查找,匹配,循环,统计,类型转换,分组。

    private Stream<Person> newStream() {
        List<Person> list = new ArrayList<>();
        list.add(Person.builder().name("zhangsan").age(12).build());
        list.add(Person.builder().name("zhangsan").age(12).build());
        list.add(Person.builder().name("lisi").age(18).build());
        list.add(Person.builder().name("wangwu").age(25).build());
        return list.stream();
    }

    /**
     * 查找
     *
     */
    @Test
    public void test_find() {
        /**
         * 返回第一个值
         */
        Optional<Person> first = newStream().findFirst();
        Assert.assertEquals(first.get(),newStream().collect(toList()).get(0));
        /**
         * 返回随机值
         */
        Optional<Person> any = newStream().findAny();
        System.out.println(any.get());
    }

    /**
     * 匹配
     */
    @Test
    public void test_match() {
        Assert.assertTrue(newStream().anyMatch(person -> person.getAge() == 18));
        Assert.assertFalse(newStream().allMatch(person -> person.getAge() == 18));
        Assert.assertFalse(newStream().noneMatch(person -> person.getAge() == 18));
    }

    /**
     * 统计
     *
     */
    @Test
    public void test_stat_max() {
        Optional<Person> maxAge = newStream().max((p1, p2) -> {
            if (p1.getAge() > p2.getAge()) {
                return 1;
            } else if (p1.getAge() < p2.getAge()) {
                return -1;
            } else {
                return 0;
            }
        });
        System.out.println(maxAge);
    }

    /**
     * 个数
     */
    @Test
    public void test_stat_count() {
        long count = newStream().count();
        System.out.println(count);
    }

    /**
     * 循环
     */
    @Test
    public void test_forEach() {
        newStream().forEach(System.out::println);
    }

    /**
     * 归并
     */
    @Test
    public void test_reduce() {
        /**
         * 第一个参数是上次结果
         * 第二个参数是元素
         */
        Optional<Integer> totalAge = newStream()
                .map(Person::getAge)
                .reduce((result, age) -> result + age);
        System.out.println(totalAge.get());

        /**
         * 第一个参数是初始值
         * 第二个参数是上次结果
         * 第三个参数是元素
         */
        Integer totalAgeWithInit = newStream()
                .map(Person::getAge)
                .reduce(10, (result, age) -> result + age);
        System.out.println(totalAgeWithInit);
    }

    /**
     * 转为数组
     *
     */
    @Test
    public void test_toArray(){
        newStream().toArray();
    }

    /**
     * 转为collection
     */
    @Test
    public void test_toCollection(){
        newStream().collect(toList());
        newStream().collect(toSet());
    }
    /**
     * 转为map
     */
    @Test
    public void test_toMap(){
        /**
         * 第一个参数是key
         * 第二个参数是value
         *
         * <p>
         *     如果有重复主键时会报错。
         *     如果Key为null会报错
         * </p>
         */
        //newStream().collect(toMap(Person::getId,person->person));
        /**
         * 第三个参数是如果key重复的时候取哪个值
         */
        newStream().collect(toMap(Person::getId,person->person,(oldValue,newValue)->oldValue));
        newStream().collect(toMap(Person::getId,person->person,(oldValue,newValue)->newValue));
        /**
         * 返回具体的map实例类型
         */
        newStream().collect(toMap(Person::getId,person->person,(oldValue,newValue)->oldValue,HashMap::new));
        newStream().collect(toMap(Person::getId,person->person,(oldValue,newValue)->oldValue,LinkedHashMap::new));
    }

    /**
     * 通过分组转为Map
     *
     * <p>
     *     分组完,一般会对元素进行聚合
     * </p>
     */
    @Test
    public void test_toMap_group(){
        /**
         * 不存在重复key报错的问题
         */
        Map<Long, List<Person>> listMap = newStream().collect(groupingBy(Person::getId));
        /**
         * 但是返回值是double
         */
        Map<Long, Double> averageAgeMap = newStream().collect(
                groupingBy(Person::getId,
                        averagingLong(Person::getAge))
        );
        /**
         * 找出最大年龄对应的Map
         */
        Map<String, Optional<Person>> maxAgeMap = newStream().collect(
                groupingBy(Person::getName,
                        maxBy(Comparator.comparing(Person::getAge)))
        );
    }

实践

可维护性

  • 点号换行,提升可阅读性
  • 不要超过3行
  • 职责单一
    @Test
    public void test_format() {
        newStream()
                .filter(person -> null == person)
                .distinct()
                .filter(person -> person.getAge() >= 18)
                .map(Person::getAge)
                .sorted((age1, age2) -> {
                    if (age1 > age2) {
                        return 1;
                    } else if (age1 < age2) {
                        return -1;
                    } else {
                        return 0;
                    }
                })
                .limit(1)
                .forEach(System.out::println);


        newStream()
                .filter(person -> null == person)
                .distinct()
                .filter(person -> person.getAge() >= 18)
                .map(Person::getAge)
                //上面排序超过三行,不容易阅读
                .sorted(Comparator.comparing(Long::valueOf))
                .limit(1)
                .forEach(System.out::println);
    }

    @Test
    public void test_single() {
        newStream()
                .filter(person -> {
                    if(null != person){
                        return true;
                    }
                    if(person.getAge() > 18){
                        return true;
                    }
                    if(person.getName().startsWith("hello")){
                        return true;
                    }
                    return false;
                })
        ;

        newStream()
                .filter(person -> null != person)
                .filter(person -> person.getAge() > 18)
                .filter(person -> person.getName().startsWith("hello"))
        ;
    }
  • 语义更明确,不需要面向每一个过程。
  • 代码更简洁,修改更方便。
    @Test
    public void test_forEach() {
        List<Person> persons = new ArrayList<>();
        List<Person> adultPersons = new ArrayList<>();
        for (Person person : persons) {
            if (person.getAge() > 18) {
                adultPersons.add(person);
            }
        }

        List<Person> adultPersons2 = persons.stream()
                .filter(person -> person.getAge() > 18)
                .collect(Collectors.toList());
    }

陷阱

    @Test
    public void test_forEach(){
        newStream().forEach(person -> {
            // 不要在forEach里做过滤的行为
            if(person.getAge()>18){
                /**
                 * 这里的return就相当于for里的continue,后面的例子还是会继续的。
                 * 无法实现break的效果,一定会把所有元素走完
                 */
                return ;
            }
            System.out.println(person);
        });
        
        newStream()
                .filter(person -> person.getAge()>18)
                .forEach(System.out::println);
    }
  • 如果有中止的需求,不要使用
  • 只做动作,不要做过滤
  • 不能用于需要索引的情景
  • 不能用于变更集合的情景
  • 使用时需要注意NPE

思考

  • 类型转换比较冗长,是否通过工具类封装或者通过重写这些子类来并且通过默认方法来进一步内置行为
  • stream和interate的区别
  • stream并发的使用

加入我们

诚邀,有梦想有激情有实力的小伙伴一起加入我们,改变世界。下面是JD的详情信息

阿里-菜鸟国际-出口大团队招新啦

posted @ 2018-08-06 10:19  庄君祥  阅读(681)  评论(1编辑  收藏  举报