Comment paginer une liste D'objets en Java 8?

avec un java.util.Listn et la page désirée taille m, je veux le transformer en une carte contenant n/m+n%m éléments. Chaque élément de la carte doit contenir m éléments.

Voici un exemple avec des entiers:

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

est-ce possible, en utilisant Java 8?

10
demandé sur Duncan Jones 2015-03-26 11:13:44

3 réponses

Vous pouvez utiliser IntStream.iterate combiné avec le toMap collecteur et le subList méthode List (merci à Duncan pour les simplifications).

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

vous calculez d'abord le nombre de touches dont vous avez besoin dans la carte. Ceci est donné par (list.size() + pageSize - 1) / pageSize (ce sera la limite du flux).

puis vous créez un flux qui crée la séquence 0, pageSize, 2* pageSize, ....

Maintenant, pour chaque valeur i vous attrapez lesubList qui sera notre valeur (vous avez besoin d'un contrôle supplémentaire pour la dernière subList pour ne pas sortir des limites du terrain) pour lequel vous carte la touche correspondante qui sera la séquence 0/pageSize, pageSize/pageSize, 2*pageSize/pageSize que vous divisez par pageSize pour obtenir la séquence naturelle 0, 1, 2, ....

Le pipeline peut être en toute sécurité exécuter en parallèle (vous devrez peut-être utiliser le toConcurrentMap collecteur à la place). Comme L'a dit Brian Goetz (merci de me le rappeler),iterate n'est pas la peine si vous voulez paralléliser le flux, donc voici un version avec 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()))));

comme avec votre exemple (une liste de 10 éléments avec une page de 3), vous obtiendrez la séquence suivante:

0, 3, 6, 9, 12, 15, ... que vous limitez à (10 + 3 - 1) / 3 = 12 / 3 = 4, qui laissent la séquence 0, 3, 6, 9. Maintenant chaque valeur est mappée à sa sous-liste correspondante:

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


Si vous voulez vraiment un Map<Integer, String> vous pouvez remplacer la fonction value mapper par
import static java.util.stream.Collectors.joining;

...

i -> list.subList(i, min(i + pageSize, list.size()))
         .stream()
         .map(Object::toString)
         .collect(joining(","))

qui vient de recueillir les éléments séparés par une virgule dans une seule Chaîne.

9
répondu Alexis C. 2017-05-23 12:10:24

solution Simple à l'aide de Goyave: com.Google.commun.recueillir.Les listes#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)));
4
répondu 卢声远 Shengyuan Lu 2015-03-27 10:02:14

comme noté dans les commentaires cela fonctionne aussi si la liste n'est pas une séquence naturelle d'entiers. Vous auriez à utiliser une générés IntStream puis et référez-vous aux éléments dans list by index.

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

nous commençons par un IntStream représentant les indices de la liste.

groupingBy regroupe les éléments par un classificateur. Dans votre cas, il regroupe x éléments par page.

mapping applique une fonction de mappage aux éléments et les recueille ensuite. Le la cartographie est nécessaire parce que joiningaccepte uniquement CharSequence. joining rejoint lui-même les éléments en utilisant un délimiteur arbitraire.

0
répondu a better oliver 2015-03-26 13:57:01