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

adr*_*mir 15 java list java-8 partition

给定一个java.util.Listn元素和所需的页面大小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));
Run Code Online (Sandbox Code Playgroud)

这是可能的,使用Java 8?

Ale*_* C. 15

您可以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()))));
}
Run Code Online (Sandbox Code Playgroud)

首先计算地图中所需的键数.这是由(list.size() + pageSize - 1) / pageSize(这将是流的限制)给出的.

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

现在,对于每个值,i您可以获取相应的值,这些值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()))));
Run Code Online (Sandbox Code Playgroud)

因此,对于您的示例(页面大小为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
Run Code Online (Sandbox Code Playgroud)


如果你真的想要一个,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(","))
Run Code Online (Sandbox Code Playgroud)

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

  • 您可以并行运行它,但是使用IntStream.iterate()启动管道会破坏您获得的任何并行性(这是一个基本的顺序源.)使用IntStream.range会更好,它与迭代的作用相同,并且分裂得更好.然后你不需要使用限制,由于它基本上依赖于遭遇顺序,因此也很难并行化. (4认同)
  • 好的解决方案 您可以将限制简化为:`..limit((list.size()+ pageSize - 1)/ pageSize)`,因为两个值都是正数(参见[this answer](http://stackoverflow.com/a/7446742)/474189)).值映射方法也可以简化为`i - > list.subList(i,Math.min(i + pageSize,list.size()))` (2认同)

卢声远*_* Lu 6

使用番石榴的简单解决方案: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)));
Run Code Online (Sandbox Code Playgroud)