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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29273705/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 07:43:24  来源:igfitidea点击:

How to paginate a list of objects in Java 8?

javalistjava-8partition

提问by adragomir

Given a java.util.Listwith nelements and a desired page size m, I want to transform it to a map containing n/m+n%melements. Each map element shall contain melements.

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

Here's an example with integers:

这是一个整数示例:

    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));

Is this possible, using Java 8?

这可能吗,使用 Java 8?

采纳答案by Alexis C.

You could use IntStream.iteratecombined with the toMapcollector and the subListmethod on List(thanks to Duncanfor the simplifications).

您可以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()))));
}

You first calculate the numbers of keys you need in the map. This is given by (list.size() + pageSize - 1) / pageSize(this will be the limit of the stream).

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

Then you create a Stream that creates the sequence 0, pageSize, 2* pageSize, ....

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

Now for each value iyou grab the corresponding subListwhich will be our value (you need an additional check for the last subListfor not getting out of bounds) for which you map the corresponding key which will be the sequence 0/pageSize, pageSize/pageSize, 2*pageSize/pageSizethat you divide by pageSizeto get the natural sequence 0, 1, 2, ....

现在,对于每个值,i您获取相应subList的值,这将是我们的值(您需要额外检查最后一个值subList,以免越界),您映射相应的键,这将是0/pageSize, pageSize/pageSize, 2*pageSize/pageSize您除以pageSize获得自然序列的序列0, 1, 2, ....

The pipeline can be safely run in parallel (you may need to use the toConcurrentMapcollector instead). As Brian Goetz commented (thanks for reminding me that), iterateis not worth if you want to parallelize the stream, so here's a version with range.

管道可以安全地并行运行(您可能需要改用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()))));


So as with your example (a list of 10 elements with a page size of 3), you'll get the following sequence:

因此,与您的示例(页面大小为 3 的 10 个元素的列表)一样,您将获得以下序列:

0, 3, 6, 9, 12, 15, ...that you limit to (10 + 3 - 1) / 3 = 12 / 3 = 4, which let the sequence 0, 3, 6, 9. Now each value is mapped to its corresponding sublist:

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>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(","))

which just collect the elements separated by a comma into a single String.

它只是将逗号分隔的元素收集到一个字符串中。

回答by a better oliver

As noted in the comments this also works if the list is not a natural sequence of integers. You would have to use a generated IntStreamthen and refer to the elements in list by index.

如评论中所述,如果列表不是整数的自然序列,这也适用。您必须使用生成的IntStreamthen 并按索引引用列表中的元素。

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"}

We start with an IntStreamrepresenting the indices of the list.

我们从IntStream表示列表索引的开始。

groupingBygroups the elements by some classifier. In your case it groups x elements per page.

groupingBy通过某个分类器对元素进行分组。在您的情况下,它将每页分组 x 个元素。

mappingapplies a mapping function to the elements and collects them afterwards. The mapping is necessary because joiningonly accepts CharSequence. joiningitself joins the elements by using an arbitrary delimiter.

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

回答by u290629

Simple solution using Guava: com.google.common.collect.Lists#partition:

使用 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)));