文档章节

JDK8漫谈——集合更强大

o
 osc_pn11u1x9
发布于 2018/08/06 10:19
字数 1739
阅读 16
收藏 0
jdk

精选30+云产品,助力企业轻松上云!>>>

解决什么问题

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

背后思想

  • 管道
  • 封装
  • 数据处理

内容说明

是什么

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

生命周期

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的详情信息

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

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。

暂无文章

Hacker News 简讯 2020-07-10

更新时间: 2020-07-10 01:15 US Supreme Court deems half of Oklahoma a Native American Reservation - (reuters.com) 美国最高法院认为俄克拉荷马州的一半是印第安人保留地 得分:131 | 评...

FalconChen
50分钟前
16
0
OSChina 周五乱弹 —— 求求你吃了我吧,不要再玩弄食物的感情了

Osc乱弹歌单(2020)请戳(这里) 【今日歌曲】 @巴拉迪维 :张喆的单曲《陷阱 》 这首歌已经在网易找不到原唱了,不知道被哪家买了版权。#今日歌曲推荐# 《陷阱 》- 张喆 手机党少年们想听歌...

小小编辑
今天
24
1
清华陈文光教授:AI 超算基准测试的最新探索和实践。

道翰天琼认知智能平台为您揭秘新一代人工智能。 无规矩不成方圆。放在超级计算机的研发领域,没有一个大家普遍接受的算力评测指标,便难以推动超算迅猛发展。 而现在伴随着人工智能的发展,大...

jackli2020
今天
7
0
@RequestMapping, consumes 提交简单有意思的测试

getParm @GetMapping("getParm")public Result getParm(String id){ System.out.println(); return ResultFactory.success(id);} 等同于 == bodyParm @PostMapping("bodyParm......

莫库什勒
今天
25
0
63. Unique Paths II

题目: 63. Unique Paths II A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). The robot can only move either down or right at any p......

JiaMing
今天
55
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部