一个对List中的数据进行分页的工具类

在项目当中,使用分页方式分批次查询数据并渲染到页面是一种相当普遍的做法。目的是避免浏览器一次性加载过多的数据导致的页面迟缓或者崩溃。另外,在数据总量过大的情况下,一次性将内容都查询出来,查询出来的结果最初是放在内存里面的,内存也没有这么大,因此很容易导致内存不够或宕机。
往往,项目中还存在另外一种分页方式,也即内存分页。内存分页的数据总量往往是已经全部加载到内存中了的。因此可以想到,内存分页场景中的需要进行分页的数据对象并不大。 那么问题就来了?我们为什么还要进行分页,是不是画蛇添足多此一举了? 事实上有没有必要进行内存分页得具体问题具体分析。笔者在写此文之前,就已经总结出两种不得不使用内存分页机制的场景:
场景(1): 数据总量虽然不大,不足以对内存或性能造成威胁,但在使用数据进行计算的过程中产生的中间数据或最终结果总量巨大,计算产生的大量临时数据或最终结果才是可能导致性能问题的关键原因。这种情况下我们也只能将总数据划分成多个更小的批次进行处理,每次只处理一个批次,这样可以使每一个小批次产生的临时数据或计算结果不足以威胁到整个计算过程。当一个批次处理结束,临时数据被销毁之后,再进行下一批次的处理。
场景(2):一次性处理数据的耗时大于处理每一个小的批次数据的耗时:比如需要将这批数据作为参数调用某个接口,数据量大,接口提供方处理速度迟缓导致的超时无法获取数据。这种情况下,分批分多次调用接口成功的概率大于一个批次单次调用接口的概率。
场景(3):第三方库、框架本身的限制。比如sql语句的in 字句不能过长,否则会导致SQL异常。这种情况下,可能需要分多次进行查询。
为此,本人在项目中封装了一个专门对List进行分页的工具类,同时也贡献出来,希望对某些人有用!!!
(1)抽象父类:

/**
 * Created by lihong10 on 2017/11/28.
 */

import java.util.Collections;
import java.util.Iterator;
import java.util.List;


/**
 * 该类用于对链表中的数据进行分页操作,采用迭代器设计模式实现。该类的其中一个子类 {@link UnRepeatablePage} 只允许分页一次。
 * 另一个子类  {@link RepeatablePage}则允许分页多次。
 *
 * <p>该类的子类适用于停车场项目是要到的另外一种分页方式,也即内存分页。正如本文术语所解释的,
 * 内存分页的数据总量往往是已经全部加载到内存中了的。因此可以想到,内存分页场景中的需要进行分页的数据总量并不大。 那么问题就来了?
 * 我们为什么还要进行分页,是不是画蛇添足多此一举了?笔者在写作之前,就已经总结出两种不得不使用内存分页机制的场景:
 * </p>
 * <ul>
 * <li>
 * 场景(1):  数据总量虽然不大,不足以对内存或性能造成威胁,但在使用数据进行计算的过程中产生的中间数据或最终结果总量巨大,
 * 计算产生的大量临时数据或最终结果才是可能导致性能问题的关键原因。这种情况下我们也只能将总数据划分成多个更小的批次进行处理,
 * 每次只处理一个批次,这样可以使每一个小批次产生的临时数据或计算结果不足以威胁到整个计算过程。当一个批次处理结束,
 * 临时数据被销毁之后,再进行下一批次的处理。
 * </li>
 * <li>
 * 场景(2):一次性处理数据的耗时大于处理每一个小的批次数据的耗时:比如需要将这批数据作为参数调用某个接口,
 * 数据量大,接口提供方处理速度迟缓导致的超时无法获取数据。这种情况下,分批分多次调用接口成功的概率大于一个批次单次调用接口的概率。
 * </li>
 * <li>
 * 场景(3):第三方库、框架本身的限制。比如sql语句的in 字句不能过长,否则会导致SQL异常。这种情况下,可能需要拆分为多次进行查询。
 * </li>
 * </ul>
 *
 * @author lihong10
 * @since 2.9.1
 */
public abstract class Page<T> implements Iterator<List<T>>, Iterable<List<T>> {
    /**
     * 原集合
     */
    protected List<T> data;

    /**
     * 当前页
     */
    protected int pageNo;

    /**
     * 该字段仅用作迭代每一分页的游标
     */
    protected int cursor;

    /**
     * 总页数
     */
    protected int totalPage;

    /**
     * 每页条数
     */
    protected int pageSize;

    /**
     * 总数据条数
     */
    protected int totalCount;

    /**
     * 双参数构造
     *
     * @param data
     * @param pageSize 分页大小
     * @author lihong10 2017年12月2日 上午10:42:30
     */
    public Page(List<T> data, int pageSize) {
        this(data, 1, pageSize);
    }

    /**
     * 三参数构造
     *
     * @param data     被分页的链表
     * @param pageNo   页码
     * @param pageSize 分页大小
     */
    public Page(List<T> data, int pageNo, int pageSize) {
        if (data == null) {
            throw new IllegalArgumentException("data can not be null!");
        }

        if (pageSize <= 0) {
            throw new IllegalArgumentException("pageSize must >= 1");
        }

        this.data = data;
        this.totalCount = data.size();
        this.pageSize = pageSize;
        this.totalPage = (totalCount + pageSize - 1) / pageSize;
        if (pageNo <= 0) {
            pageNo = 1;
        } else if (pageNo > this.totalPage) {
            pageNo = totalPage;
        }
        this.pageNo = pageNo;
        this.cursor = 0;
    }

    /**
     * 返回迭代器对象
     *
     * @return
     * @author lihong10 2017年12月3日 上午10:42:30
     */
    @Override
    public final Iterator<List<T>> iterator() {
        return this;
    }

    @Override
    public void remove() {
    }

    /**
     * @return
     * @author lihong10 2017年12月3日 上午10:42:30
     * 得到pageNo表示的那一页数据
     */
    public final List<T> getPagedList() {
        check();
        int fromIndex = (pageNo - 1) * pageSize;
        if (fromIndex >= data.size()) {
            return Collections.emptyList();//空数组
        }
        if (fromIndex < 0) {
            return Collections.emptyList();//空数组
        }
        int toIndex = pageNo * pageSize;
        if (toIndex >= data.size()) {
            toIndex = data.size();
        }
        return data.subList(fromIndex, toIndex);
    }

    /**
     * @param pageNo
     * @return
     * @author lihong10 2017年12月3日 上午10:42:30
     * 根据页数获取指定分页
     * @author lihong10 2017年11月30日 下午15:42:30
     */
    public final List<T> getPage(int pageNo) {
        check();
        if (pageNo <= 0) {
            pageNo = 1;
        } else if (pageNo > totalPage) {
            pageNo = totalPage;
        }

        int fromIndex;
        int toIndex;
        if (pageNo * pageSize < totalCount) {// 判断是否为最后一页
            toIndex = pageNo * pageSize;
            fromIndex = toIndex - pageSize;
        } else {
            toIndex = totalCount;
            fromIndex = pageSize * (totalPage - 1);
        }

        List<T> objects = null;
        if (data != null && data.size() > 0) {
            objects = data.subList(fromIndex, toIndex);
        }

        return objects;
    }

    /**
     * 重置游标,默认重置为0
     *
     * @return
     * @author lihong10 2017年12月3日 上午10:42:30
     */
    public abstract void reset();

    /**
     * 重置游标操作
     *
     * @param cursor
     * @return
     * @author lihong10 2017年12月3日 上午10:42:30
     */
    public abstract void reset(int cursor);

    /**
     * 判断某个方法是否允许被调用
     *
     * @return
     * @author lihong10 2017年12月3日 上午10:42:30
     */
    public abstract void check();

    public int getPageSize() {
        return pageSize;
    }

    public List<T> getData() {
        return data;
    }

    public int getPageNo() {
        return pageNo;
    }

    public int getTotalPage() {
        return totalPage;
    }

    public int getTotalCount() {
        return totalCount;
    }
}


(2)可重复分页的子类及示例:
/**
 * Created by lihong10 on 2017/11/28.
 */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 用法参见{@link UnRepeatablePage} 与  {@link Page}
 *
 * @param <T>
 * @author lihong10 2017年12月4日 上午10:42:30
 */
public class RepeatablePage<T> extends Page<T> {

    public RepeatablePage(List<T> data, int pageSize) {
        this(data, 1, pageSize);
    }

    public RepeatablePage(List<T> data, int currentPage, int pageSize) {
        super(data, currentPage, pageSize);
    }

    @Override
    public boolean hasNext() {
        if (totalPage <= 0) {
            return false;
        }
        boolean hasNext = cursor < totalPage;
        if (hasNext == false) {
            reset(); //重置的目的,是为了可以重复调用for循环
        }
        return hasNext;
    }

    @Override
    public List<T> next() {
        int pageNo = this.cursor + 1;
        List<T> page = getPage(pageNo);
        this.cursor++;
        return page;
    }

    public void check() {
    }

    public void reset() {
        this.cursor = 0;
    }

    public void reset(int cursor) {
        if (cursor < 0) {
            this.cursor = 0;
        } else if (cursor >= totalPage) {
            this.cursor = totalPage;
        } else {
            this.cursor = cursor;
        }
    }

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(new Integer[]{0, 1, 2, 3, 4, 5, 6});
        int pageSize = 3;//遍历分页时候,指定分页大小是3
        RepeatablePage<Integer> page = new RepeatablePage<Integer>(list, pageSize);

        System.out.println("------------直接获取某一页------------------------");
        System.out.println(page.getPage(1));
        System.out.println(page.getPage(2));
        System.out.println(page.getPage(3));


        System.out.println("第1次遍历结果是");
        for (List<Integer> li : page) { //for遍历
            System.out.println(li);
        }

        System.out.println("第2次遍历结果是");
        for (List<Integer> li : page) {
            System.out.println(li);
        }

        System.out.println("第3次遍历结果是");
        for (List<Integer> li : page) {
            System.out.println(li);
        }

        List<String> li = new ArrayList<>();
        RepeatablePage<String> pageEmpty = new RepeatablePage(li, pageSize);
        for (List<String> sub : pageEmpty) {
        }
        System.out.println("-----------------");


        List<String> li2 = new ArrayList<>();
        li2.add(null);
        li2.add(null);
        RepeatablePage<String> pageEmpty2 = new RepeatablePage(li2, pageSize);
        for (List<String> sub : pageEmpty2) {
            System.out.println(sub);
        }
        System.out.println("-----------------");

    }

}


(3)不可重复分页的子类及示例:

import java.util.Arrays;
import java.util.List;

/**
 * 用法参见{@link RepeatablePage} 与  {@link Page}
 *
 * @param <T>
 * @author lihong10 2017年12月4日 上午10:42:30
 */
public class UnRepeatablePage<T> extends Page<T> {

    public UnRepeatablePage(List<T> data, int pageSize) {
        this(data, 1, pageSize);
    }


    public UnRepeatablePage(List<T> data, int pageNo, int pageSize) {
        super(data, pageNo, pageSize);
    }

    @Override
    public boolean hasNext() {
        return cursor < this.totalPage;
    }

    @Override
    public List<T> next() {
        cursor++;
        if (data == null) {
            return null;
        }

        List<T> subList = null;
        if (data.size() > pageSize) {
            subList = data.subList(0, pageSize);
            data = data.subList(pageSize, data.size());
        } else {
            subList = data.subList(0, data.size());
            data = null;
        }

        return subList;
    }

    public void check() {
        if (cursor > 0) {
            throw new IllegalStateException("the operation is not permitted if for cycle or next() method" +
                    " has benn called before ");
        }
    }

    @Override
    public void reset() {
        throw new UnsupportedOperationException("reset is not permitted !!!," +
                "if you have to, use RepeatablePage.class");
    }

    @Override
    public void reset(int cursor) {
        throw new UnsupportedOperationException("reset is not permitted !!!," +
                "if you have to, use RepeatablePage.class ");
    }

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(new Integer[]{0, 1, 2, 3, 4, 5, 6});
        int pageSize = 6;//遍历分页时候,指定分页大小是3
        UnRepeatablePage<Integer> page = new UnRepeatablePage<Integer>(list, 3);

        System.out.println("------------遍历前允许直接获取某一页------------------------");
        System.out.println(page.getPage(2));


        System.out.println("第1次遍历结果是");
        for (List<Integer> li : page) { //for遍历
            System.out.println(li);
        }
        System.out.println("------------遍历过之后,不允许直接获取某一页------------------------");
        System.out.println(page.getPage(2));

        page.reset();//调用reset会抛出异常,应为该子类不允许这个操作
        for (List<Integer> li : page) { //for遍历
            System.out.println(li);
        }
    }

}






  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值