• 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

  • 相关阅读:
    HYSBZ 2818 gcd
    hdu1695 GCD
    HYSBZ 2301
    poj 2096
    正则表达式匹配HTML标签或标记
    [转载]Sed 命令详解 正则表达式元字符
    [转载]Shell删除各种注释的脚本
    [转载]sed实现直接修改文件内容
    [转载]强大的grep用法详解:grep与正则表达式
    [转载]【Shell脚本】逐行处理文本文件
  • 原文地址:https://www.cnblogs.com/yihuihui/p/14468334.html
Copyright © 2020-2023  润新知