Ad esempio ci sono raccolte [1,2,3,4,5]
, [6,7,8]
, [9,0]
. Un modo per evitare i loop con gli iteratori per interlacciare queste raccolte tramite Java 8 stream API per ottenere il seguente risultato: [1,6,9,2,7,0,3,8,4,5]
?E 'possibile .flatMap() o .collect() in modo uniforme raccolte multiple
risposta
io non sono sicuro se c'è un modo più semplice con l'API Stream, ma si può fare questo usando un flusso sopra gli indici di tutte le liste di prendere in considerazione:
static <T> List<T> interleave(List<List<T>> lists) {
int maxSize = lists.stream().mapToInt(List::size).max().orElse(0);
return IntStream.range(0, maxSize)
.boxed()
.flatMap(i -> lists.stream().filter(l -> i < l.size()).map(l -> l.get(i)))
.collect(Collectors.toList());
}
Questo ottiene la dimensione del più grande elencare negli elenchi dati. Per ogni indice, viene quindi mappato piano con un flusso formato dagli elementi di ciascuna lista in quell'indice (se l'elemento esiste).
Quindi è possibile utilizzare con
public static void main(String[] args) {
List<Integer> list1 = Arrays.asList(1,2,3,4,5);
List<Integer> list2 = Arrays.asList(6,7,8);
List<Integer> list3 = Arrays.asList(9,0);
System.out.println(interleave(Arrays.asList(list1, list2, list3))); // [1, 6, 9, 2, 7, 0, 3, 8, 4, 5]
}
Utilizzando la libreria protonpack, è possibile utilizzare il metodo interleave
di fare proprio questo:
List<Stream<Integer>> lists = Arrays.asList(list1.stream(), list2.stream(), list3.stream());
List<Integer> result = StreamUtils.interleave(Selectors.roundRobin(), lists).collect(Collectors.toList());
System.out.println(result);
Non una soluzione molto buona però. Basta una prova
List<String> listA = Arrays.asList("a1","a2","a3","a4");
List<String> listB = Arrays.asList("b1","b2","b3","b4");
List<String> listC = Arrays.asList("c1","c2","c3","c4");
List<String> combined = Stream.of(listA, listB, listC).flatMap(Collection::stream).collect(Collectors.toList());
AtomicInteger index = new AtomicInteger();
List<String> list1 = combined
.stream().filter(x -> index.incrementAndGet() % 4 ==1).collect(Collectors.toList());
index.set(0);
List<String> list2 = combined
.stream().filter(x -> index.incrementAndGet() % 4 ==2).collect(Collectors.toList());
index.set(0);
List<String> list3 = combined
.stream().filter(x -> index.incrementAndGet() % 4 ==3).collect(Collectors.toList());
index.set(0);
List<String> list0 = combined
.stream().filter(x -> index.incrementAndGet() % 4 ==0).collect(Collectors.toList());
List<String> desiredOutput = Stream.of(list1, list2, list3,list0).flatMap(Collection::stream).collect(Collectors.toList());
System.out.println(String.join(",", desiredOutput));
mi piace la risposta di Tunaki e hanno lavorato un po 'più su di esso:
static <T> List<T> interleave(List<? extends Collection<T>> lists) {
int maxSize = lists.stream().mapToInt(Collection::size).max().orElse(0);
List<Iterator<T>> iterators = lists.stream().map(l -> l.stream().iterator()).collect(Collectors.toList());
return IntStream.range(0, maxSize)
.boxed()
.flatMap(i -> iterators.stream().filter(it -> it.hasNext()).map(it -> it.next()))
.collect(Collectors.toList());
}
volevo vedere se siamo in grado di trattare le liste iniziali come flussi troppo, si è scoperto che può. Come effetto collaterale, evitiamo get(i)
in un elenco (se fosse davvero molto lungo LinkedList
, vorremmo evitarlo). Non ho trovato un modo per farlo senza la conoscenza di una lunghezza massima delle liste originali.
Se le liste sono lunghe e non ad accesso casuale (ad esempio, LinkedList), ci sarà una penalità di prestazioni. Nella prima soluzione, cioè, non so del secondo. Poi di nuovo, LinkedList a volte funziona male, quindi forse non così tanto nuovo. –