2015-03-26 3 views
9

Gegeben eine java.util.List mit n Elemente und eine gewünschte Seitengröße m, ich möchte es in eine Karte mit n/m+n%m Elemente verwandeln. Jedes Kartenelement muss m Elemente enthalten.Wie paginiert man eine Liste von Objekten in Java 8?

Hier ist ein Beispiel mit ganzen Zahlen:

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

Ist dies möglich, mit Hilfe von Java 8?

+1

Was Sie bisher versucht haben? Bitte lesen Sie [Wie stelle ich eine gute Frage?] (Http://stackoverflow.com/help/how-to-ask). – DavidPostill

+0

Also habe ich in Collectors :: partitioningBy geschaut, aber das teilt eine Liste mit einem Prädikat auf. Ich habe das gefragt, weil ich nicht weiß, wo ich in Java 8 anfangen soll, um das zu erreichen. – adragomir

+1

@ user3030447 Sind Sie sicher, dass Sie eine 'Map ' und nicht eine 'Map >' wollen? Sie können die Liste während einer Präsentationsphase immer in eine commited Zeichenfolge umwandeln ... –

Antwort

7

könnten Sie IntStream.iterate mit dem toMap Sammler kombiniert verwenden und die subList Methode auf List (dank Duncan für die Vereinfachungen).

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

Sie zuerst die Anzahl der Schlüssel berechnen Sie in der Karte benötigen. Dies wird durch (list.size() + pageSize - 1)/pageSize angegeben (dies ist die Grenze des Streams). Dann erstellen Sie einen Stream, der die Sequenz 0, pageSize, 2* pageSize, ... erstellt.

nun für jeden Wert i Sie die entsprechenden subList greifen, die unser Wert sein wird (Sie müssen eine zusätzliche Kontrolle für die letzte subList für nicht außerhalb der Grenzen bekommen), für die Sie die entsprechende Taste zuordnen, die die Folge sein wird 0/pageSize, pageSize/pageSize, 2*pageSize/pageSize dass Sie teilen durch pageSize, um die natürliche Sequenz 0, 1, 2, ... zu erhalten.

Die Pipeline kann sicher parallel betrieben werden (möglicherweise müssen Sie stattdessen den Kollektor toConcurrentMap verwenden). Als Brian Goetz kommentierte (danke, dass er mich daran erinnert hat), iterate ist es nicht wert, wenn du den Stream parallelisieren willst, also hier ist eine Version mit 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 wie bei Ihrem Beispiel (eine Liste von 10 Elementen mit einer Seitengröße von 3), erhalten Sie die folgende Sequenz erhalten:

0, 3, 6, 9, 12, 15, ..., die Sie (10 + 3 - 1)/3 = 12/3 = 4 begrenzen, die lassen die Sequenz 0, 3, 6, 9. Nun wird jeder Wert auf seine entsprechenden sublist abgebildet:

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 


Wenn Sie wirklich wollen ein Map<Integer, String> Sie den Wert Mapperfunktion mit

import static java.util.stream.Collectors.joining; 

... 

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

ersetzen könnte, die nur die durch ein Komma getrennt Elemente sammeln in ein einzelner String.

+0

Dies ist die Ausgabe {0 = [1, 2, 3], 1 = [4, 5, 6], 2 = [7, 8, 9], 3 = [10]} für die erste Version. Alexis, du bist ein SCHÖNES Baby, danke, dass du es mir gezeigt hast. Ich werde den Code verstehen und mein Wissen verbessern :) Ich wünsche Ihnen alles Gute – adragomir

+1

@ user3030447 Ich habe hinzugefügt, wie es sich mit Ihrem entsprechenden Beispiel verhält. –

+2

Gute Lösung. Sie können Ihr Limit folgendermaßen vereinfachen: '.limit ((list.size() + pageSize - 1)/pageSize)', da beide Werte positiv sind (siehe [diese Antwort] (http://stackoverflow.com/a/7446742)/474189)). Die Methode für die Wertezuordnung könnte auch vereinfacht werden: i -> list_List (i, Math.min (i + pageSize, list.size())) –

0

Wie in den Kommentaren erwähnt, funktioniert dies auch, wenn die Liste keine natürliche Folge von ganzen Zahlen ist. Sie müssten dann einen generierten IntStream verwenden und auf die Elemente in der Liste nach Index verweisen.

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

Wir beginnen mit einem IntStream die Indizes der Liste repräsentiert.

groupingBy gruppiert die Elemente durch einen Klassifikator. In Ihrem Fall gruppiert es x Elemente pro Seite.

mapping wendet eine Mapping-Funktion auf die Elemente an und sammelt sie anschließend. Das Mapping ist notwendig, da joining nur CharSequence akzeptiert. joining selbst verbindet die Elemente mit einem beliebigen Begrenzer.

+0

Das funktioniert gut, wenn Sie eine natürliche Sequenz in der Liste haben, aber mit der Liste [1, -1, 5, 2] und einer Seitengröße von 2 erhalten Sie nicht die gewünschten Ergebnisse. –

+0

@AlexisC. Das stimmt, aber in diesem Fall können Sie einen generierten 'IntStream' verwenden und auf die ursprüngliche Liste nach Index verweisen. Der Algorithmus würde sich jedoch nicht ändern. – zeroflagL

+0

@zeroflagL Warum aktualisieren Sie Ihre Antwort nicht, um das zu zeigen? –

2

Einfache Lösung mit 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)));