zoukankan      html  css  js  c++  java
  • Java中两种分页遍历的使用姿势

    Java中两种分页遍历的使用姿势

    在日常开发中,分页遍历迭代的场景可以说非常普遍了,比如扫表,每次捞100条数据,然后遍历这100条数据,依次执行某个业务逻辑;这100条执行完毕之后,再加载下一百条数据,直到扫描完毕

    那么要实现上面这种分页迭代遍历的场景,我们可以怎么做呢

    本文将介绍两种使用姿势

    • 常规的使用方法
    • 借助Iterator的使用姿势

    1. 数据查询模拟

    首先mock一个分页获取数据的逻辑,直接随机生成数据,并且控制最多返回三页

    public static int cnt = 0;
    
    private static List<String> randStr(int start, int size) {
        ++cnt;
        if (cnt > 3) {
            return Collections.emptyList();
        } else if (cnt == 3) {
            cnt = 0;
            size -= 2;
        }
    
        System.out.println("======================= start to gen randList ====================");
        List<String> ans = new ArrayList<>(size);
        for (int i = 0; i < size; i++) {
            ans.add((start + i) + "_" + UUID.randomUUID().toString());
        }
        return ans;
    }
    

    2. 基本实现方式

    针对这种场景,最常见也是最简单直观的实现方式

    • while死循环
    • 内部遍历
    private static void scanByNormal() {
        int start = 0;
        int size = 5;
        while (true) {
            List<String> list = randStr(start, size);
            for (String str : list) {
                System.out.println(str);
            }
    
            if (list.size() < size) {
                break;
            }
            start += list.size();
        }
    }
    

    3. 迭代器实现方式

    接下来介绍一种更有意思的方式,借助迭代器的遍历特性来实现,首先自定义一个通用分页迭代器

    public static abstract class MyIterator<T> implements Iterator<T> {
        private int start = 0;
        private int size = 5;
    
        private int currentIndex;
        private boolean hasMore = true;
        private List<T> list;
    
        public MyIterator() {
        }
    
        @Override
        public boolean hasNext() {
            if (list != null && list.size() > currentIndex) {
                return true;
            }
    
            // 当前的数据已经加载完毕,尝试加载下一批
            if (!hasMore) {
                return false;
            }
    
            list = load(start, size);
            if (list == null || list.isEmpty()) {
                // 没有加载到数据,结束
                return false;
            }
    
            if (list.size() < size) {
                // 返回条数小于限制条数,表示还有更多的数据可以加载
                hasMore = false;
            }
    
            currentIndex = 0;
            start += list.size();
            return true;
        }
    
        @Override
        public T next() {
            return list.get(currentIndex++);
        }
    
        public abstract List<T> load(int start, int size);
    }
    

    接下来借助上面的迭代器可以比较简单的实现我们的需求了

    private static void scanByIterator() {
        MyIterator<String> iterator = new MyIterator<String>() {
            @Override
            public List<String> load(int start, int size) {
                return randStr(start, size);
            }
        };
    
        while (iterator.hasNext()) {
            String str = iterator.next();
            System.out.println(str);
        }
    }
    

    那么问题来了,上面这种使用方式比前面的优势体现再哪儿呢?

    • 双层循环改为单层循环

    接下来接入重点了,在jdk1.8引入了函数方法 + lambda之后,又提供了一个更简洁的使用姿势

    public class IteratorTestForJdk18 {
    
        @FunctionalInterface
        public interface LoadFunc<T> {
            List<T> load(int start, int size);
        }
    
        public static class MyIterator<T> implements Iterator<T> {
            private int start = 0;
            private int size = 5;
    
            private int currentIndex;
            private boolean hasMore = true;
            private List<T> list;
            private LoadFunc<T> loadFunc;
    
            public MyIterator(LoadFunc<T> loadFunc) {
                this.loadFunc = loadFunc;
            }
    
            @Override
            public boolean hasNext() {
                if (list != null && list.size() > currentIndex) {
                    return true;
                }
    
                // 当前的数据已经加载完毕,尝试加载下一批
                if (!hasMore) {
                    return false;
                }
    
                list = loadFunc.load(start, size);
                if (list == null || list.isEmpty()) {
                    // 没有加载到数据,结束
                    return false;
                }
    
                if (list.size() < size) {
                    // 返回条数小于限制条数,表示还有更多的数据可以加载
                    hasMore = false;
                }
    
                currentIndex = 0;
                start += list.size();
                return true;
            }
    
            @Override
            public T next() {
                return list.get(currentIndex++);
            }
        }
    }
    

    在jdk1.8及之后的使用姿势,一行代码即可

    private static void scanByIteratorInJdk8() {
        new MyIterator<>(IteratorTestForJdk18::randStr)
            .forEachRemaining(System.out::println);
    }
    

    这次对比效果是不是非常显眼了,从此以后分页迭代遍历再也不用冗长的双重迭代了

    II. 其他

    1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

    一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

    2. 声明

    尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

    3. 扫描关注

    一灰灰blog

    QrCode

  • 相关阅读:
    java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header.
    spring-session-data-redis依赖冲突问题
    centos7启动iptables时报Job for iptables.service failed because the control process exited with error cod
    图片上传后台服务报内存溢出 Out Of Memory Java heap space
    mysql 数据库密码忘记重置 进行远程连接
    打Jar包
    Type interface com.innovationV2.mapper.UserMapper is not known to the MapperRegistry
    关于java基础类型Integer String的clone()
    clion使用clang编译
    token & refresh token 机制总结
  • 原文地址:https://www.cnblogs.com/yihuihui/p/14468334.html
Copyright © 2011-2022 走看看