关于数据分页
Posted prayjourney
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于数据分页相关的知识,希望对你有一定的参考价值。
分页在我们平时处理数据的时候,是非常常见的操作,尤其是SQL,比如使用limit m,n这样的语句,又比如在elastic search之中,我们需要对search的结果进行分页,这都是一些很常见的场景,其实数据的分页,最根本的原因就是,如果当数据量很大的时候,我们如果一次性放入到内存之中操作,这样必然会对内存,数据库,前端展示等方面造成很大的压力,降低用户体验,甚至无法得到正确的结果,所以我们需要分页,把一个巨大的数据,按照我们的规划,多次获取这个数据,使用“蚂蚁搬家”的方式获取数据,每次我们拿到的数据的量,就叫做是页面大小,而我们每一次拿到的是不同的数据,这些数据是承载在不同的记录之中,这个记录就叫做页面编号。抛开数据库和ES不说,我们平时常用的List和Map,其实也是可以分页的,通过对于List和Map的分页,我们可以去理解分页的原理。
分页的两个关键问题,一个是页面大小,也就是一页的数据量有多少,二是按照上述的页面大小,一个数据可以分多少个页面,我们可以通过如下例子分析,比如我们有15个数据,如果每页有5个数据,也就是页面大小设置为5,那么就有15/5=3
,并且15%5=0
,这意味着,我们的规划,可以完全分页,而没有凑不满一页的情况;如果我们每页有7个数据,那么就有15/7=2
,并且15%7=1
,那么就意味着,我们有2个完整的页面,同时,还有一个残缺的页面,这个页面只有1个数据。
所以在分页的时候,需要确定可以分多少页,更深一个层次就是意味着,能不能完整的分页。如果可以,那么就是数据数量/页面大小
个页面,如果不能完整分页,那么就(数据数量/页面大小)+1
个页面。这就是问题的关键,一般而言,我们是要从0页面开始,这比较符合我们的一般操作习惯,还有就是边界情况,页面不能小于0,也不能大于我们可以分页的页面。分析完毕之后,上代码:
public class PageUtil {
/**
* 获取分页的总数
*
* @param ls 需要分页的list
* @param pageSize 页面大小
* @return 按照此页面分页可以得到的分页数量
*/
public static <T> int getIteratorNum(List<T> ls, int pageSize) {
int base = ls.size() / pageSize;
boolean complete = (ls.size() % pageSize) == 0 ? true : false;
if (complete == true) {
return base;
} else {
return base + 1;
}
}
/**
* 获取分页之后,该页的数据
*
* @param list 需要分页的List数据
* @param pageSize 页面大小
* @param pageNo 页面编码
* @param <T> T参数类型
* @return pageNo页面的内容,相当于是一个子List
* @throws Exception exception
*/
public static <T> List<T> list2Page(List<T> list, int pageSize, int pageNo) throws Exception {
// 获取当前分页条件下的页面数量
int allPageNumber = getIteratorNum(list, pageSize);
if (pageNo < 0 || pageNo > allPageNumber) {
// 也可直接报错
throw new Exception("page number is out of boundary!");
} else if (pageNo >= 0 && pageNo < allPageNumber - 1) {
return list.subList(pageNo * pageSize, pageNo * pageSize + pageSize);
} else {
// (pageNo > 0 && pageNo == iteratorNum)
return list.subList(pageNo * pageSize, list.size());
}
}
/**
* 获取分页的总数
*
* @param map 需要分页的map
* @param pageSize 页面大小
* @return 按照此页面分页可以得到的分页数量
*/
public static <T,V> int getIteratorNum(Map<T,V> map, int pageSize) {
int base = map.size() / pageSize;
boolean complete = (map.size() % pageSize) == 0 ? true : false;
if (complete == true) {
return base;
} else {
return base + 1;
}
}
/**
* map的分页
*
* @param map 需要分页的数据
* @param pageSize 页面大小
* @param pageNo 要获取的页面编码
* @param <T> T参数类型
* @param <V> V参数类型
* @return pageNo页面的内容,相当于是一个子Map
* @throws Exception exception
*/
public static <T,V> Map<T, V> map2Page(Map<T,V> map, int pageSize, int pageNo) throws Exception {
// 获取当前分页条件下的页面数量
int allPageNumber = getIteratorNum(map, pageSize);
// 需要先把Map转换成List, 然后去遍历,这样可以让操作有顺序的进行
Set<T> ts = map.keySet();
List<T> keyList = new ArrayList<>();
List<V> valueList = new ArrayList<>();
for (T t: ts){
keyList.add(t);
valueList.add(map.get(t));
}
if (pageNo < 0 || pageNo > allPageNumber) {
throw new Exception("page number is out of boundary!");
} else if (pageNo >= 0 && pageNo < allPageNumber - 1) {
List<T> tempKeyList = keyList.subList(pageNo * pageSize, pageNo * pageSize + pageSize);
List<V> tempValueList = valueList.subList(pageNo * pageSize, pageNo * pageSize +pageSize);
Map<T, V> tvHashMap = new HashMap<>();
for (int i =0; i< tempKeyList.size(); i++){
// 1.此处可以Map的方式get, 2.可以通过list按顺序获取
tvHashMap.put(tempKeyList.get(i), tempValueList.get(i));
}
return tvHashMap;
} else {
List<T> tempKeyList = keyList.subList(pageNo * pageSize, map.size());
List<V> tempValueList = valueList.subList(pageNo * pageSize, map.size());
Map<T, V> tvHashMap = new HashMap<>();
for (int i =0; i< tempKeyList.size(); i++){
// 1.此处可以Map的方式get, 2.可以通过list按顺序获取
tvHashMap.put(tempKeyList.get(i), tempValueList.get(i));
}
return tvHashMap;
}
}
}
对于List和Map,其实是Java层面的内容,和分页的逻辑关系不大。需要注意的是,对于Map分页的时候,我们使用两个List来取的Key和此Key对应的Value,当然我们可以通过Map.get(key)这种方式来获取,按照上述的使用两个list,其实是意义对应的,key和value的关系是一一对应的,他们之间的对应关系和在两个list之中的顺序,是一致的。
以上是关于关于数据分页的主要内容,如果未能解决你的问题,请参考以下文章