Java中兩種分頁遍歷的使用姿勢

Java中兩種分頁遍歷的使用姿勢

在日常開發中,分頁遍歷迭代的場景可以說非常普遍了,比如掃表,每次撈100條數據,然後遍歷這100條數據,依次執行某個業務邏輯;這100條執行完畢之後,再加載下一百條數據,直到掃描完畢

那麼要實現上面這種分頁迭代遍歷的場景,我們可以怎麼做呢

本文將介紹兩種使用姿勢

  • 常規的使用方法
  • 藉助Iterator的使用姿勢

<!-- more -->

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章