在日常开发中,分页遍历迭代的场景可以说非常普遍了,比如扫表,每次捞100条数据,然后遍历这100条数据,依次执行某个业务逻辑;这100条执行完毕之后,再加载下一百条数据,直到扫描完毕
那么要实现上面这种分页迭代遍历的场景,我们可以怎么做呢
本文将介绍两种使用姿势
1. 数据查询模拟 首先mock一个分页获取数据的逻辑,直接随机生成数据,并且控制最多返回三页
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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. 基本实现方式 针对这种场景,最常见也是最简单直观的实现方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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. 迭代器实现方式 接下来介绍一种更有意思的方式,借助迭代器的遍历特性来实现,首先自定义一个通用分页迭代器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 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) ; }
接下来借助上面的迭代器可以比较简单的实现我们的需求了
1 2 3 4 5 6 7 8 9 10 11 12 13 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之后,又提供了一个更简洁的使用姿势
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 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及之后的使用姿势,一行代码即可
1 2 3 4 private static void scanByIteratorInJdk8 () { new MyIterator<>(IteratorTestForJdk18::randStr) .forEachRemaining(System.out::println); }
这次对比效果是不是非常显眼了,从此以后分页迭代遍历再也不用冗长的双重迭代了
II. 其他 一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明 尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
3. 扫描关注 一灰灰blog