Explain in detail the use posture of two paging traversal in Java
- 2021-09-05 00:12:51
- OfStack
In daily development, the scenario of paging traversal iteration can be said to be very common, such as scanning tables, fishing for 100 pieces of data at a time, then traversing these 100 pieces of data, and executing a certain business logic in turn; After these 100 pieces are executed, load another 100 pieces of data until the scan is completed
So what can we do to achieve the above scenario of paging iterative traversal
This article will introduce two gestures
Conventional usage With the help of Iterator's use posture
1. Data query simulation
First of all, mock1 has the logic of obtaining data by paging, directly and randomly generating data, and controlling the return of up to 3 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
For this scenario, the most common and simple and intuitive implementation method
while dead 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, a more interesting way is introduced, which is realized by the traversal characteristics of iterators. First, a general paging iterator is customized
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 following 1 Batch
if (!hasMore) {
return false;
}
list = load(start, size);
if (list == null || list.isEmpty()) {
// Data is not loaded, end
return false;
}
if (list.size() < size) {
// The number of returned pieces is less than the limit, indicating that there is more data to load
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 above iterator, 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);
}
}
Then the question comes, where is the advantage of the above usage over the previous one?
Change double-layer cycle to single-layer cycle
Next, the key point is that after jdk1.8 introduces the function method + lambda, it provides a more concise use gesture
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 following 1 Batch
if (!hasMore) {
return false;
}
list = loadFunc.load(start, size);
if (list == null || list.isEmpty()) {
// Data is not loaded, end
return false;
}
if (list.size() < size) {
// The number of returned pieces is less than the limit, indicating that there is more data to load
hasMore = false;
}
currentIndex = 0;
start += list.size();
return true;
}
@Override
public T next() {
return list.get(currentIndex++);
}
}
}
To use gestures in jdk 1.8 and beyond, 1 line of code is enough
private static void scanByIteratorInJdk8() {
new MyIterator<>(IteratorTestForJdk18::randStr)
.forEachRemaining(System.out::println);
}
Is this comparison very obvious? From now on, paging iterative traversal will no longer need lengthy double iterations