分页从0开始还是从1开始?

在持久层、Java集合运算里面,分页是很常见的操作。
用户对分页最直白的需求是怎样的?可以参考浏览器的搜索结果展示。页码从1开始,第几页就展示第几页的内容。
使用下来发现有些分页是从零开始,有些从一开始。有些可以用页码跳到指定页,有些需要计算offset,因此将所有情况都捋一捋,并且记录互相转换方式以便查询。

数据

id
1
2
3
4

MySQL limit offset

1
2
3
4
5
6
7
8
9
10
11
select id from xxx limit 2 offset 1
# 结果:2 3

select id from xxx limit 2 offset 3
# 结果:4

select id from xxx limit 2 offset 2
# 结果:3 4 (每页2条,第2页)

# 假设需要查询每页n条,第m页
# select id from xxx limit <n> offset <(m-1) x n>

Spring JPA Pageable

Spring Controller支持从url的size、page、sort等参数构建Pageable,通过JPA请求MySQL。那么用户需要的每页数量、页码是怎样传递的呢?

size,page,sort的值会直接用于生成Pageable,也就是和PageRequest.of(page, size)一样。
需要注意,page=0查的是第1页,page=1查到的结果已经是第2页了。

比“MySQL limit offset”方便的是,页码帮算好了,不需要计算offset值。

Java Stream

某些场景下需要从数据库捞全量数据,在内存中做分页。
使用Java Stream的skip、limit方法可以方便地对数据做切片。skip的参数类似“MySQL limit offset”的offset值。因为Pageable的页码已经减一,所以这里只要skip “每页数量 x 页码”即可。

从Pageable到Stream切片转换的例子如下:

1
2
3
IntStream.range(0, 20)
.skip(pageable.getPageSize() * (pageable.getPageNumber()))
.limit(pageable.getPageSize()).forEach(System.out::println);

参考资料