2016-05-30 27 views
13

Ho uno Collection di elementi di una classe arbitraria. Voglio scorrere la collezione ed eseguire alcune operazioni utilizzando l'elemento e ogni altro elemento della raccolta uno per uno (escludendo l'elemento stesso). Lascia che sia List<Integer> per semplicità:Java 8 stream: elaborare ogni coppia possibile di elementi dall'elenco

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); 

Con for spire magnetiche sarà:

for (Integer i : list) { 
    for (Integer j : list) { 
     if (!i.equals(j)) System.out.println(i * 2 + j); //just for example 
    } 
} 

la domanda è come farlo con il Stream API?

Questo è quello che sono venuto a:

list.stream().forEach(i -> 
    list.stream().forEach(j -> { 
     if (!i.equals(j)) System.out.println(i * 2 + j); 
    }) 
); 

non sembra meglio di ciclo nidificato però. C'è un modo più elegante?

risposta

13

È possibile eseguire questa operazione utilizzando l'operazione flatMap:

list.stream() 
    .flatMap(i -> list.stream().filter(j -> !i.equals(j)).map(j -> i * 2 + j)) 
    .forEach(System.out::println); 

Questo codice è la creazione di un flusso di lista di input. Esegue il mapping di ogni elemento dell'elenco con uno stream creato dallo stesso elenco, in cui l'elemento corrente è stato filtrato e ogni elemento di questo nuovo elenco è il risultato dell'operazione i * 2 + j.

Tutti gli elementi vengono quindi stampati sulla console.

6

È possibile evitare il confronto degli oggetti eseguendo prima il mapping ai valori int, ad es.

list.stream().mapToInt(Integer::intValue) 
    .flatMap(i -> list.stream().filter(j -> j!=i).mapToInt(j -> i*2 + j)) 
    .forEach(System.out::println); 

Ma in realtà, si sta eseguendo le operazioni condizionali e controlli di uguaglianza per qualcosa che è invariante. Come l'intera operazione si basa sulla lista sorgente non cambia il suo contenuto in-tra, si può semplicemente generare gli indici elenco degli accoppiamenti, in primo luogo:

final int end=list.size(); 
IntStream.range(0, end).flatMap(i -> 
    IntStream.concat(IntStream.range(0, i), IntStream.range(i+1, end)) 
     .map(j -> list.get(i) * 2 + list.get(j))) 
    .forEach(System.out::println); 

Nel caso in cui non si vuole calcolare un valore numerico, ma creare un oggetto per le coppie, diventa un po 'più complicato a causa del fatto che IntStream non ha l'operazione flatMapToObj, quindi abbiamo bisogno di una combinazione di mapToObj e flatMap (a meno che non usiamo boxed). Così la costruzione di una delle due lunghezze gamma esemplare si presenta come:

IntStream.range(0, end).mapToObj(i -> 
    IntStream.concat(IntStream.range(0, i), IntStream.range(i+1, end)) 
     .mapToObj(j -> new int[]{ list.get(i), list.get(j)})) 
     .flatMap(Function.identity()) 
    .forEach(a -> System.out.println(a[0]*2+a[1])); 

Naturalmente, per un elenco semplice come [ 1, 2, 3, 4, 5 ], potremmo semplicemente usare IntStream.rangeClosed(1, 5) in primo luogo, non si tratta di un List:

IntStream.rangeClosed(1, 5).flatMap(i -> 
     IntStream.concat(IntStream.range(1, i), IntStream.rangeClosed(i+1, 5)) 
     .map(j -> i*2 + j)) 
    .forEach(System.out::println); 
Problemi correlati