2015-03-11 9 views
10

Avendo una lista di stringhe, ho bisogno di costruire una lista di oggetti che sono effettivamente coppie (string, its position in the list). Attualmente ho un tale codice utilizzando le raccolte di google:Come mappare gli elementi della lista ai loro indici usando i flussi Java 8?

public Robots(List<String> names) { 
    ImmutableList.Builder<Robot> builder = ImmutableList.builder(); 
    for (int i = 0; i < names.size(); i++) { 
     builder.add(new Robot(i, names.get(i))); 
    } 
    this.list = builder.build(); 
} 

Mi piacerebbe farlo utilizzando Java 8 stream. Se non ci fosse alcun indice, ho potuto solo fare:

public Robots(List<String> names) { 
    this.list = names.stream() 
      .map(Robot::new) // no index here 
      .collect(collectingAndThen(
        Collectors.toList(), 
        Collections::unmodifiableList 
      )); 
} 

Per ottenere l'indice, che avrei dovuto fare qualcosa di simile:

public Robots(List<String> names) { 
    AtomicInteger integer = new AtomicInteger(0); 
    this.list = names.stream() 
      .map(string -> new Robot(integer.getAndIncrement(), string)) 
      .collect(collectingAndThen(
        Collectors.toList(), 
        Collections::unmodifiableList 
      )); 
} 

Tuttavia, la documentazione dice che la funzione di mappatura dovrebbe essere senza stato , ma lo AtomicInteger è effettivamente il suo stato.

C'è un modo per mappare gli elementi del flusso sequenziale alle loro posizioni nello stream?

risposta

15

Si potrebbe fare qualcosa di simile:

public Robots(List<String> names) { 
    this.list = IntStream.range(0, names.size()) 
         .mapToObj(i -> new Robot(i, names.get(i))) 
         .collect(collectingAndThen(toList(), Collections::unmodifiableList)); 
} 

Tuttavia potrebbe non essere così efficiente a seconda dell'implementazione di fondo della lista. È possibile prendere un iteratore da IntStream; quindi chiamare next() nello mapToObj.

In alternativa, la biblioteca proton-pack definisce la funzionalità zipWithIndex per i flussi:

this.list = StreamUtils.zipWithIndex(names.stream()) 
         .map(i -> new Robot(i.getIndex(), i.getValue())) 
         .collect(collectingAndThen(toList(), Collections::unmodifiableList)); 
+0

Se stavo usando 'next()' da ' Iterator' nella funzione di mappatura, sarebbe stato. 'zipWithIndex' sembra interessante - non usa la lista direttamente, quindi funzionerà con qualsiasi flusso. Grazie! –

+1

@JaroslawPawlak Sì, è vero. E sì se l'elenco sottostante è un 'LinkedList' per esempio; otterrai cattive prestazioni. Il 'zipWithIndex' restituisce un flusso con indici' Long'; se hai veramente bisogno di 'Integer', puoi usare il metodo zip fornendo un' IntStream' (potenzialmente) infinito :-) –

8

Il modo più semplice è quello di trasmettere gli indici:

List<Robot> robots = IntStream.range(0, names.size()) 
           .mapToObj(i -> new Robot(i, names.get(i)) 
           .collect(toList()); 
Problemi correlati