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. 一灰灰Blog: https://liuyueyi.github.io/hexblog
一灰灰的個人博客,記錄所有學習和工作中的博文,歡迎大家前去逛逛
2. 聲明
盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
- 微博地址: 小灰灰Blog
- QQ: 一灰灰/3302797840
3. 掃描關注
一灰灰blog