Two usage postures of page traversal in Java

Two usage postures of page traversal in Java

In daily development, the scenario of paging traversal iteration can be said to be very common. For example, scan the table, retrieve 100 data each time, and then traverse these 100 data to execute a certain business logic in turn; After the execution of these 100 items, load the next 100 items of data until the scanning is completed

So what can we do to realize the above scenario of paging iterative traversal

This article will introduce two kinds of posture

  • Conventional usage
  • Using the posture of Iterator

<!-- more -->

1. Data query simulation

First, mock a logic to obtain data by paging, directly generate data randomly, and control to return up to three pages

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. Basic implementation mode

For this scenario, the most common and simple and intuitive implementation method

  • while loop
  • Internal traversal
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. Iterator implementation

Next, we introduce a more interesting way to implement it with the help of the traversal characteristics of iterators. First, customize a general paging iterator

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;
        }

        // The current data has been loaded. Try to load the next batch
        if (!hasMore) {
            return false;
        }

        list = load(start, size);
        if (list == null || list.isEmpty()) {
            // No data loaded, end
            return false;
        }

        if (list.size() < size) {
            // The number of returned entries is less than the limit, which indicates that more data can be loaded
            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);
}

Next, with the help of the iterator above, we can easily realize our requirements

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);
    }
}

So the question is, where are the advantages of the above way of use compared with the previous way?

  • Double layer cycle changed to single layer cycle

Next, access the key points in jdk1 8 after introducing function method + lambda, it provides a more concise use posture

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;
            }

            // The current data has been loaded. Try to load the next batch
            if (!hasMore) {
                return false;
            }

            list = loadFunc.load(start, size);
            if (list == null || list.isEmpty()) {
                // No data loaded, end
                return false;
            }

            if (list.size() < size) {
                // The number of returned entries is less than the limit, which indicates that more data can be loaded
                hasMore = false;
            }

            currentIndex = 0;
            start += list.size();
            return true;
        }

        @Override
        public T next() {
            return list.get(currentIndex++);
        }
    }
}

In jdk1 8 and later use posture, one line of code is enough

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

Is the contrast effect very conspicuous? From then on, there will be no need for lengthy double iterations

II. other

1. A gray Blog: https://liuyueyi.github.io/hexblog

A gray personal blog, which records all blog posts in study and work. Welcome to visit

2. Declaration

The above contents are only the words of one family. Due to limited personal ability, it is inevitable that there are omissions and mistakes. If you find a bug or have better suggestions, you are welcome to criticize and correct and be grateful

3. Scan attention

A gray blog

Tags: Java JDK Spring github Next.js QRCode imgs

Posted by saunders1989 on Thu, 14 Apr 2022 00:42:05 +0930