Java编程语言,使用迭代器Iterator实现自动分页查询

一、背景

在Java中,Iterator是一种设计模式,用于提供一种按顺序访问集合中元素的方式,而不暴露集合的底层表示。Iterator接口主要用于遍历集合,它定义了两种方法:hasNext()和next()。

借助于迭代器Iterator,可以逐页迭代API返回的数据,而不需要用户手动处理分页逻辑。

二、迭代器Iterator

迭代器模式是一种行为设计模式, 让你能在不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素。

  • hasNext()
    目的:hasNext()方法用来检测迭代器是否还有下一个元素可以迭代。
    返回值:返回一个布尔值,如果存在下一个元素则返回true,否则返回false。
    使用场景:在调用next()方法之前使用hasNext()可以避免发生NoSuchElementException。
  • next()
    目的:next()方法用来获取迭代器当前指向的下一个元素。
    返回值:返回迭代器中的下一个元素。
    异常:如果没有下一个元素,即在hasNext()返回false的情况下调用next(),将抛出NoSuchElementException。
  • 示例用法
Iterator<String> iterator = someCollection.iterator();
while(iterator.hasNext()) {
    String element = iterator.next();
    // 对element进行操作
}

三、自动分页查询

下图是一个迭代器的框架

在这里插入图片描述

1、遍历每页,直到下一页的数据为空

因为查询是通用的功能,所以这里使用的是泛型,而非具体类。

    public <T> List<T> getAll(final String url, final Class<T[]> type) {
        List<T> results = new ArrayList<>();
        Iterator<T[]> iterator = asIterator(url, type);

        while (iterator.hasNext()) {
            T[] requests = iterator.next();

            if (requests.length > 0) {
                results.addAll(Arrays.asList(requests));
            }
        }
        return results;
    }

2、实现迭代器

  • 有两个成员变量:next(下一个要返回的对象)和url(当前要请求的URL)
  • hasNext(),先调用fetch()方法来获取下一个数据,当返回的数组类型,则要求数组的长度大于0;否则next对象必须非空。
  • next(),它先调用fetch()来确保next已经被赋值,返回next,并将next重置为null。所以,本方法额外使用了一个变量record,用来存储本次查询返回值。- - 不能直接使用next对象。
   public <T> Iterator<T> asIterator(final String requestUrl, final Class<T> type) {
        return new Iterator<T>() {
            T next;
            URL url = new URL(requestUrl);

            @Override
            public boolean hasNext() {
                fetch();
                if (next != null && next.getClass().isArray()) {
                    Object[] arr = (Object[]) next;
                    return arr.length != 0;
                } else {
                    return next != null;
                }
            }

            @Override
            public T next() {
                fetch();
                T record = next;

                if (record == null) {
                    throw new NoSuchElementException();
                }

                next = null;
                return record;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
  • fetch()方法,它会对next进行赋值,如果已有值,则返回。同时,它会组装好查询下一页的请求url。
			private void fetch() {
                if (next != null) {
                    return;
                }

                if (url == null) {
                    return;
                }

                try {
                    try {
                        next = query(url, type);
                        assert next != null;
                        findNextUrl();
                    } catch (IOException e) {
                        handleAPIError(e, connection);
                    }
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
            }
  • findNextUrl(),组装下一页的请求url。使用了正则表达式PAGE_PATTERN来查找和替换URL中的分页页码,即参数page。
private static final Pattern PAGE_PATTERN = Pattern.compile("([&|?])page=(\\d+)");

			private void findNextUrl() throws MalformedURLException {
                String url = this.url.toString();

                this.url = null;
                /* Increment the page number for the url if a "page" property exists,
                 * otherwise, add the page property and increment it.
                 * The Gitlab API is not a compliant hypermedia REST api, so we use
                 * a naive implementation.
                 */
                Matcher matcher = PAGE_PATTERN.matcher(url);

                if (matcher.find()) {
                    Integer page = Integer.parseInt(matcher.group(2)) + 1;
                    this.url = new URL(matcher.replaceAll(matcher.group(1) + "page=" + page));
                } else {
                    // Since the page query was not present, its safe to assume that we just
                    // currently used the first page, so we can default to page 2
                    this.url = new URL(url + (url.indexOf('?') > 0 ? '&' : '?') + "page=2");
                }
            }

四、另一种分页查询的实现

使用spring 的 Pageable逐页查询,Jpa的实现示例见下:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

Page<Course> findByModifiedDateGreaterThanEqualAndModifiedDateLessThanEqual(Date startDate, Date endDate, Pageable request);

和迭代器Iterator的实现类似,这里使用do…while循环,利用next()方法一直往后查询,直到记录为空。

    private void sync(Date lastSyncDate, Date thisSyncDate) {
        Page<Course> coursePage;

        Pageable page = PageRequest.of(0, 100, Sort.Direction.ASC, "modifiedDate");
        do {
            coursePage = courseRepository.findByModifiedDateGreaterThanEqualAndModifiedDateLessThanEqual(
                    lastSyncDate,
                    thisSyncDate,
                    page);
            List<Course> list = coursePage.getContent();
            page = page.next();
        } while (!coursePage.isEmpty());
    }

  • 23
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值