2016-06-08 35 views

risposta

8

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

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. –

0

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

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.