关于数据分页

分页在我们平时处理数据的时候,是非常常见的操作,尤其是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之中的顺序,是一致的。

相关推荐