在日常开发中,分页遍历迭代的场景可以说非常普遍了,比如扫表,每次捞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