如何对 Java 8 中的对象列表进行分页?
How to paginate a list of objects in Java 8?
给定具有 n
元素的 java.util.List
和所需的页面大小 m
,我想将其转换为包含 n/m+n%m
元素的地图。每个地图元素应包含 m
个元素。
这是一个整数示例:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// What is the equivalent Java 8 code to create the map below from my list?
Map<Integer, List<Integer>> map = new HashMap<>();
map.put(0, Arrays.asList(1,2,3));
map.put(1, Arrays.asList(4,5,6));
map.put(2, Arrays.asList(7,8,9));
map.put(3, Arrays.asList(10));
这可能吗,使用 Java 8?
您可以将 IntStream.iterate
与 toMap
收集器和 subList
方法结合使用 List
(感谢 Duncan 的简化)。
import static java.util.stream.Collectors.toMap;
import static java.lang.Math.min;
...
static Map<Integer, List<Integer>> partition(List<Integer> list, int pageSize) {
return IntStream.iterate(0, i -> i + pageSize)
.limit((list.size() + pageSize - 1) / pageSize)
.boxed()
.collect(toMap(i -> i / pageSize,
i -> list.subList(i, min(i + pageSize, list.size()))));
}
您首先计算地图中所需的键数。这是由 (list.size() + pageSize - 1) / pageSize
给出的(这将是流的限制)。
然后您创建一个创建序列 0, pageSize, 2* pageSize, ...
的 Stream。
现在对于每个值 i
你获取对应的 subList
这将是我们的值(你需要额外检查最后一个 subList
是否没有越界)你映射相应的键,这将是你除以 pageSize
的序列 0/pageSize, pageSize/pageSize, 2*pageSize/pageSize
得到自然序列 0, 1, 2, ...
.
管道可以安全地 运行 并行(您可能需要改用 toConcurrentMap
收集器)。正如 Brian Goetz 评论的那样(感谢您提醒我),如果您想并行化流,iterate
是不值得的,所以这里有一个带有 range
.
的版本
return IntStream.range(0, (list.size() + pageSize - 1) / pageSize)
.boxed()
.collect(toMap(i -> i ,
i -> list.subList(i * pageSize, min(pageSize * (i + 1), list.size()))));
对于您的示例(页面大小为 3 的 10 个元素的列表),您将得到以下序列:
0, 3, 6, 9, 12, 15, ...
你限制为(10 + 3 - 1) / 3 = 12 / 3 = 4
,这让序列0, 3, 6, 9
。现在每个值都映射到其对应的子列表:
0 / pageSize = 0 -> list.subList(0, min(0 + pageSize, 10)) = list.subList(0, 3);
3 / pageSize = 1 -> list.subList(3, min(3 + pageSize, 10)) = list.subList(3, 6);
6 / pageSize = 2 -> list.subList(6, min(6 + pageSize, 10)) = list.subList(6, 9);
9 / pageSize = 3 -> list.subList(9, min(9 + pageSize, 10)) = list.subList(6, 10);
^
|
this is the edge-case for the last sublist to
not be out of bounds
如果你真的想要一个 Map<Integer, String>
你可以用 替换值映射器函数
import static java.util.stream.Collectors.joining;
...
i -> list.subList(i, min(i + pageSize, list.size()))
.stream()
.map(Object::toString)
.collect(joining(","))
它只是将以逗号分隔的元素收集到一个字符串中。
如评论中所述,如果列表不是整数的自然序列,这也适用。然后,您必须使用生成的 IntStream
并按索引引用列表中的元素。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Map<Integer, String> map = IntStream
.range(0, list.size())
.boxed()
.collect(groupingBy(
i -> i / 3, //no longer i-1 because we start with 0
mapping(i -> list.get((int) i).toString(), joining(","))
));
//result: {0="1,2,3", 1="4,5,6", 2="7,8,9", 3="10"}
我们从代表列表索引的 IntStream
开始。
groupingBy
按某个分类器对元素进行分组。在您的情况下,它每页对 x 个元素进行分组。
mapping
对元素应用映射函数,然后收集它们。映射是必要的,因为 joining
只接受 CharSequence
。 joining
本身使用任意定界符连接元素。
使用 Guava 的简单解决方案:com.google.common.collect.Lists#partition:
List<List<Integer>> partition = Lists.partition(list, 3); //<- here
Map map = IntStream.range(0, partition.size()).boxed().collect(Collectors.toMap(
Function.identity(),
i -> partition.get(i)));
给定具有 n
元素的 java.util.List
和所需的页面大小 m
,我想将其转换为包含 n/m+n%m
元素的地图。每个地图元素应包含 m
个元素。
这是一个整数示例:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// What is the equivalent Java 8 code to create the map below from my list?
Map<Integer, List<Integer>> map = new HashMap<>();
map.put(0, Arrays.asList(1,2,3));
map.put(1, Arrays.asList(4,5,6));
map.put(2, Arrays.asList(7,8,9));
map.put(3, Arrays.asList(10));
这可能吗,使用 Java 8?
您可以将 IntStream.iterate
与 toMap
收集器和 subList
方法结合使用 List
(感谢 Duncan 的简化)。
import static java.util.stream.Collectors.toMap;
import static java.lang.Math.min;
...
static Map<Integer, List<Integer>> partition(List<Integer> list, int pageSize) {
return IntStream.iterate(0, i -> i + pageSize)
.limit((list.size() + pageSize - 1) / pageSize)
.boxed()
.collect(toMap(i -> i / pageSize,
i -> list.subList(i, min(i + pageSize, list.size()))));
}
您首先计算地图中所需的键数。这是由 (list.size() + pageSize - 1) / pageSize
给出的(这将是流的限制)。
然后您创建一个创建序列 0, pageSize, 2* pageSize, ...
的 Stream。
现在对于每个值 i
你获取对应的 subList
这将是我们的值(你需要额外检查最后一个 subList
是否没有越界)你映射相应的键,这将是你除以 pageSize
的序列 0/pageSize, pageSize/pageSize, 2*pageSize/pageSize
得到自然序列 0, 1, 2, ...
.
管道可以安全地 运行 并行(您可能需要改用 toConcurrentMap
收集器)。正如 Brian Goetz 评论的那样(感谢您提醒我),如果您想并行化流,iterate
是不值得的,所以这里有一个带有 range
.
return IntStream.range(0, (list.size() + pageSize - 1) / pageSize)
.boxed()
.collect(toMap(i -> i ,
i -> list.subList(i * pageSize, min(pageSize * (i + 1), list.size()))));
对于您的示例(页面大小为 3 的 10 个元素的列表),您将得到以下序列:
0, 3, 6, 9, 12, 15, ...
你限制为(10 + 3 - 1) / 3 = 12 / 3 = 4
,这让序列0, 3, 6, 9
。现在每个值都映射到其对应的子列表:
0 / pageSize = 0 -> list.subList(0, min(0 + pageSize, 10)) = list.subList(0, 3);
3 / pageSize = 1 -> list.subList(3, min(3 + pageSize, 10)) = list.subList(3, 6);
6 / pageSize = 2 -> list.subList(6, min(6 + pageSize, 10)) = list.subList(6, 9);
9 / pageSize = 3 -> list.subList(9, min(9 + pageSize, 10)) = list.subList(6, 10);
^
|
this is the edge-case for the last sublist to
not be out of bounds
如果你真的想要一个
Map<Integer, String>
你可以用 替换值映射器函数
import static java.util.stream.Collectors.joining;
...
i -> list.subList(i, min(i + pageSize, list.size()))
.stream()
.map(Object::toString)
.collect(joining(","))
它只是将以逗号分隔的元素收集到一个字符串中。
如评论中所述,如果列表不是整数的自然序列,这也适用。然后,您必须使用生成的 IntStream
并按索引引用列表中的元素。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Map<Integer, String> map = IntStream
.range(0, list.size())
.boxed()
.collect(groupingBy(
i -> i / 3, //no longer i-1 because we start with 0
mapping(i -> list.get((int) i).toString(), joining(","))
));
//result: {0="1,2,3", 1="4,5,6", 2="7,8,9", 3="10"}
我们从代表列表索引的 IntStream
开始。
groupingBy
按某个分类器对元素进行分组。在您的情况下,它每页对 x 个元素进行分组。
mapping
对元素应用映射函数,然后收集它们。映射是必要的,因为 joining
只接受 CharSequence
。 joining
本身使用任意定界符连接元素。
使用 Guava 的简单解决方案:com.google.common.collect.Lists#partition:
List<List<Integer>> partition = Lists.partition(list, 3); //<- here
Map map = IntStream.range(0, partition.size()).boxed().collect(Collectors.toMap(
Function.identity(),
i -> partition.get(i)));