Dividi l’elenco in più elenchi con un numero fisso di elementi in java 8

Voglio qualcosa che sia simile alla funzione scala raggruppata. Fondamentalmente, scegli 2 elementi alla volta e elaborali. Ecco un riferimento per lo stesso:

Dividi l’elenco in più elenchi con un numero fisso di elementi

Lambdas fornisce cose come groupingBy e partitioningBy ma nessuno di loro sembra fare la stessa cosa della funzione raggruppata in Scala. Qualsiasi suggerimento sarebbe apprezzato.

Sembra un problema gestito meglio come un’operazione Stream basso livello, proprio come gli op forniti dalla stessa API Stream . Una soluzione (relativa) semplice può essere simile a:

 public static  Stream> chunked(Stream s, int chunkSize) { if(chunkSize<1) throw new IllegalArgumentException("chunkSize=="+chunkSize); if(chunkSize==1) return s.map(Collections::singletonList); Spliterator src=s.spliterator(); long size=src.estimateSize(); if(size!=Long.MAX_VALUE) size=(size+chunkSize-1)/chunkSize; int ch=src.characteristics(); ch&=Spliterator.SIZED|Spliterator.ORDERED|Spliterator.DISTINCT|Spliterator.IMMUTABLE; ch|=Spliterator.NONNULL; return StreamSupport.stream(new Spliterators.AbstractSpliterator>(size, ch) { private List current; @Override public boolean tryAdvance(Consumer> action) { if(current==null) current=new ArrayList<>(chunkSize); while(current.size() 

Semplice test:

 chunked(Stream.of(1, 2, 3, 4, 5, 6, 7), 3) .parallel().forEachOrdered(System.out::println); 

Il vantaggio è che non è necessaria una raccolta completa di tutti gli articoli per la successiva elaborazione del stream, ad es

 chunked( IntStream.range(0, 1000).mapToObj(i -> { System.out.println("processing item "+i); return i; }), 2).anyMatch(list->list.toString().equals("[6, 7]"))); 

stamperà:

 processing item 0 processing item 1 processing item 2 processing item 3 processing item 4 processing item 5 processing item 6 processing item 7 true 

piuttosto che elaborare migliaia di elementi di IntStream.range(0, 1000) . Ciò consente anche l'utilizzo di Stream sorgenti infiniti:

 chunked(Stream.iterate(0, i->i+1), 2).anyMatch(list->list.toString().equals("[6, 7]"))); 

Se sei interessato a una raccolta completamente materializzata invece di applicare le successive operazioni di Stream , puoi semplicemente utilizzare la seguente operazione:

 List list=Arrays.asList(1, 2, 3, 4, 5, 6, 7); int listSize=list.size(), chunkSize=2; List> list2= IntStream.range(0, (listSize-1)/chunkSize+1) .mapToObj(i->list.subList(i*=chunkSize, listSize-chunkSize>=i? i+chunkSize: listSize)) .collect(Collectors.toList()); 

Puoi usare la libreria Guava .

List bigList = ... List> smallerLists = Lists.partition(bigList, 10);

È ansible creare il proprio raccoglitore. Qualcosa come questo:

 class GroupingCollector implements Collector>, List>> { private final int elementCountInGroup; public GroupingCollector(int elementCountInGroup) { this.elementCountInGroup = elementCountInGroup; } @Override public Supplier>> supplier() { return ArrayList::new; } @Override public BiConsumer>, T> accumulator() { return (lists, integer) -> { if (!lists.isEmpty()) { List integers = lists.get(lists.size() - 1); if (integers.size() < elementCountInGroup) { integers.add(integer); return; } } List list = new ArrayList<>(); list.add(integer); lists.add(list); }; } @Override public BinaryOperator>> combiner() { return (lists, lists2) -> { List> r = new ArrayList<>(); r.addAll(lists); r.addAll(lists2); return r; }; } @Override public Function>, List>> finisher() { return lists -> lists; } @Override public Set characteristics() { return Collections.emptySet(); } } 

E poi puoi usarlo in un modo come questo:

  List> collect = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).collect(new GroupingCollector<>(3)); System.out.println(collect); 

Stamperà:

[[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]

Sarebbe anche ansible una soluzione ricorsiva per trasformare la lista in una lista di liste

 int chunkSize = 2; private  List> process(List list) { if (list.size() > chunkSize) { List chunk = list.subList(0, chunkSize); List rest = list.subList(chunkSize, list.size()); List> lists = process(rest); return concat(chunk, lists); } else { ArrayList> retVal = new ArrayList<>(); retVal.add(list); return retVal; } } private  List> concat(List chunk, List> rest) { rest.add(0, chunk); return rest; } 

Potresti scrivere il tuo finisher da collezione, simile a

 final List strings = Arrays.asList("Hello", "World", "I", "Am", "You"); final int size = 3; final List> stringLists = strings.stream() .collect(Collectors.collectingAndThen(Collectors.toList(), new Function, List>>() { @Override public List> apply(List strings) { final List> result = new ArrayList<>(); int counter = 0; List stringsToAdd = new ArrayList<>(); for (final String string : strings) { if (counter == 0) { result.add(stringsToAdd); } else { if (counter == size) { stringsToAdd = new ArrayList<>(); result.add(stringsToAdd); counter = 0; } } ++counter; stringsToAdd.add(string); } return result; } })); System.out.println("stringLists = " + stringLists); // stringLists = [[Hello, World, I], [Am, You]] 

Una versione semplice con java 8 streams api:

 static  List> partition(List list, Integer partitionSize) { int numberOfLists = BigDecimal.valueOf(list.size()) .divide(BigDecimal.valueOf(partitionSize), 0, CEILING) .intValue(); return IntStream.range(0, numberOfLists) .mapToObj(it -> list.subList(it * partitionSize, Math.min((it+1) * partitionSize, list.size()))) .collect(Collectors.toList()); }