今天主要和大家分享的是本人总结的分页执行方法,也可以说就是分批执行;该篇采用java8新增的表达式来操作,希望能给各位带来好的帮助和在日常工作中提供便利;同样的操作流程和逻辑之前用C#代码写过一次,有需要的朋友可以看以前的博文;
- 分页方式拆分List为多个子集List方法
- 执行统一方法-无返回值,关键字:Consumer
- 执行统一方法-有返回值,关键字:Function
- 分批并行执行方法 采用:Executors+分页方法
分页方式拆分List为多个子集List方法
这里我将会分享两种拆分的方法,一种传统subList来分割list数据,一种采用stream流来拆分;先来看第一种整体代码:
1 /** 2 * list 分成多个子集list 3 * @param list 4 * @param pageCount 总页数 ,如果list条数不够,根据list条数来 5 * @param <T> 6 * @return 7 */ 8 public static <T> List<List<T>> funcToPage(List<T> list, int pageCount) { 9 10 int currentCount = list.size(); 11 //总页数 真实线程数 12 int page = pageCount >= currentCount ? currentCount : pageCount; 13 //每页条数 14 int pageSize = currentCount / page; 15 16 List<List<T>> pageList = new ArrayList<>(); 17 for (int i = 0; i < page; i++) { 18 //subList分页 19 List<T> lastT = new ArrayList<>(); 20 //分页部分 21 lastT.addAll(list.subList(i * pageSize, (i + 1) * pageSize)); 22 23 //把剩余部分没拆到每批次中 24 if (page * pageSize + i < currentCount) { 25 lastT.addAll(list.subList(page * pageSize + i, page * pageSize + i + 1)); 26 } 27 28 pageList.add(lastT); 29 } 30 return pageList; 31 }
这里通过按分页的方式来分割list数据,分页几乎是每个系统都会用到的方式;再来看看新特性分割的方法:
1 public static <T> List<List<T>> funcToPage(List<T> list, int pageCount) { 2 3 int currentCount = list.size(); 4 //总页数 真实线程数 5 int page = pageCount >= currentCount ? currentCount : pageCount; 6 //每页条数 7 int pageSize = currentCount / page; 8 9 List<List<T>> pageList = new ArrayList<>(); 10 for (int i = 0; i < page; i++) { 11 12 //采用流方式分页 13 List<T> lastPage = list.stream(). 14 skip(i * pageSize). 15 limit(pageSize). 16 collect(Collectors.toList()); 17 18 if (page * pageSize + i < currentCount) { 19 lastPage.add( 20 list.stream(). 21 skip(page * pageSize + i). 22 limit(1). 23 findFirst(). 24 get() 25 ); 26 } 27 pageList.add(lastPage); 28 } 29 return pageList; 30 }
通过stream流蓝分割,这里用到了skip(),limit()两个方法来分页,这两方法分别表示:跳到某条数据开始和限制多少条数据,最后有个findFirst()方法表示:查找第一条数据,看起来和C#的linq写法类似;
执行统一方法-无返回值,关键字:Consumer
这里要分享的关于list(当然也可以是其他)数据循环执行某个方法,这里用到了Consumer提供的accept方法来接受参数,代码如下:
1 /** 2 * list 不返回值 3 * @param list 数据源 4 * @param consumer 5 * @param <T> 参数类型 6 */ 7 public static <T> void funcToNoResult(List<T> list, Consumer<T> consumer) { 8 list.forEach(b -> consumer.accept(b)); 9 }
测试用例:
1 List<Integer> list = Arrays.asList(1, 2, 3); 2 FuncUtil.funcToNoResult(list, b -> { 3 System.out.println("funcToNoResult这是:" + b); 4 });
执行统一方法-有返回值,关键字:Function
和上面一样这个方法也主要是对list数据在某个表达式中操作,但是该方法有返回值的概念,就是说先通过执行了表达式后,能够得到执行的返回结果:
1 /** 2 * list 有返回值 3 * @param list 数据源 4 * @param function 5 * @param <T> 参数类型 6 * @param <V> 返回类型 7 * @return 8 */ 9 public static <T, V> List<V> funcToResult(List<T> list, Function<T, V> function) { 10 List<V> results = new ArrayList<>(); 11 list.forEach(b -> results.add(function.apply(b))); 12 return results; 13 }
关键字Function,要得到返回值主要是通过function.apply(b)方法来获取,如下测试用例:
1 FuncUtil.funcToResult(list, b -> { 2 return "funcToResult这是:" + b; 3 }).forEach(b -> System.out.println(b));
分批并行执行方法 采用:Executors+分页方法
这里来到今天分享的重点,该方法主要可拆分为以下几个步骤:分批数据源-》分批并行执行-》合并结果;分配数据采用的就是上面的分页方法,下面具体看代码:
1 /** 2 * 分批执行方法 采用:Executors+分页方法 3 * @param list 数据源 4 * @param function 处理方法 lamda表达式 5 * @param maxThreadCount 处理线程数量 6 * @param <T> 参数类型 7 * @param <V> 返回类型 8 * @return 处理完成结果集 9 */ 10 public static <T, V> List<V> funcToExecutorPageSubmits(List<T> list, Function<T, V> function, int maxThreadCount) { 11 List<V> results = new ArrayList<>(); 12 13 if (list == null || list.isEmpty()) { 14 return results; 15 } 16 17 maxThreadCount = maxThreadCount <= 0 ? 1 : maxThreadCount; 18 maxThreadCount = maxThreadCount >= 10 ? 10 : maxThreadCount; 19 20 //分批数据源 21 List<List<T>> pageList = funcToPage(list, maxThreadCount); 22 23 //分批并行执行 24 List<Future<List<V>>> futures = new ArrayList<>(); 25 ExecutorService executorService = Executors.newFixedThreadPool(pageList.size()); 26 pageList.forEach(items -> { 27 futures.add( 28 executorService.submit(() -> { 29 List<V> childResults = new ArrayList<>(); 30 items.forEach(item -> childResults.add(function.apply(item))); 31 return childResults; 32 }) 33 ); 34 }); 35 36 //合并结果 37 futures.forEach(b -> { 38 try { 39 results.addAll(b.get()); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } catch (ExecutionException e) { 43 e.printStackTrace(); 44 } 45 }); 46 47 return results; 48 }
并行执行就是用了ExecuteService+Future来接受结果,通过executorService.submit()提交执行某一批次数据需要执行的方法,最后再汇总结果;
因为有这种常见的场景:某一批数据或者url需要在执行某个按钮事件时去调用第三方接口并同步返回数据给界面用户,因此就有了上面方法的设计和产生。就目前该方法已被我用在了生产环境中,暂无什么异常或者问题;以下是测试用例:
1 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8); 2 FuncUtil.funcToExecutorPageSubmits( 3 //数据源 4 list 5 , 6 //待执行方法 7 b -> { 8 return Thread.currentThread().getName() + ":" + b; 9 }, 10 6). 11 //输出返回结果 12 forEach(b -> System.out.println(b));