如何在Java 8中对对象列表进行分页?

给定一个包含n元素和所需页面大小为mjava.util.List ,我想将其转换为包含n/m+n%m元素的映射。 每个地图元素应包含m元素。

这是一个整数的例子:

  List 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> 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.iteratetoMap收集器和List上的subList方法结合使用(感谢Duncan的简化)。

 import static java.util.stream.Collectors.toMap; import static java.lang.Math.min; ... static Map> partition(List 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 (这将是流的限制)。

然后创建一个Stream,创建序列0, pageSize, 2* pageSize, ...

现在对于每个值,你获取相应的subList ,它将是我们的值(你需要额外检查最后一个subList以便不越界),你为它映射相应的键,它将是序列0/pageSize, pageSize/pageSize, 2*pageSize/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你可以用值替换值mapper函数

 import static java.util.stream.Collectors.joining; ... i -> list.subList(i, min(i + pageSize, list.size())) .stream() .map(Object::toString) .collect(joining(",")) 

它只是将用逗号分隔的元素收集到一个String中。

使用Guava的简单解决方案: com.google.common.collect.Lists#partition :

  List> partition = Lists.partition(list, 3); //<- here Map map = IntStream.range(0, partition.size()).boxed().collect(Collectors.toMap( Function.identity(), i -> partition.get(i))); 

正如评论中所指出的,如果列表不是自然的整数序列,这也有效。 您必须使用生成的IntStream然后按索引引用列表中的元素。

 List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Map 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按一些分类器对元素进行groupingBy 。 在您的情况下,它每页分组x个元素。

mapping将映射函数应用于元素并在之后收集它们。 映射是必要的,因为joining只接受CharSequence 。 join本身通过使用任意分隔符来连接元素。