2016-01-09 6 views
11

In Scala, si può facilmente fare una mappa in parallelo, forEach, ecc, con:Operazioni parallele sulle collezioni Kotlin?

collection.par.map(..) 

Esiste un equivalente in Kotlin?

+4

Alcune delle raccolte parallele più veloci in giro provengono da GS-Collections: https://github.com/goldmansachs/gs-collections ... che è possibile utilizzare da Kotlin (come qualsiasi framework di raccolta Java può essere utilizzato) –

risposta

21

La libreria standard Kotlin non ha il supporto per le operazioni parallele . Tuttavia, poiché Kotlin utilizza le classi di raccolta Java standard, è possibile utilizzare l'API di flusso Java 8 per eseguire anche operazioni parallele su raccolte Kotlin.

+2

come si può usare l'API di flusso Java 8 in Kotlin? – LordScone

+1

@LordScone Nello stesso modo in cui lo faresti in Java. E.g .: 'myCollection.parallelStream(). Map {...}. filter {...} ' –

10

Non v'è alcun supporto ufficiale in stdlib di Kotlin ancora, ma si potrebbe definire un extension function di imitare par.map:

fun <T, R> Iterable<T>.pmap(
      numThreads: Int = Runtime.getRuntime().availableProcessors() - 2, 
      exec: ExecutorService = Executors.newFixedThreadPool(numThreads), 
      transform: (T) -> R): List<R> { 

    // default size is just an inlined version of kotlin.collections.collectionSizeOrDefault 
    val defaultSize = if (this is Collection<*>) this.size else 10 
    val destination = Collections.synchronizedList(ArrayList<R>(defaultSize)) 

    for (item in this) { 
     exec.submit { destination.add(transform(item)) } 
    } 

    exec.shutdown() 
    exec.awaitTermination(1, TimeUnit.DAYS) 

    return ArrayList<R>(destination) 
} 

(github source)

Ecco un semplice esempio di utilizzo

val result = listOf("foo", "bar").pmap { it+"!" }.filter { it.contains("bar") } 

Se necessario, consente di modificare il threading fornendo il numero di thread o anche uno specifico java.util.concurrent.Executor. Per esempio.

listOf("foo", "bar").pmap(4, transform = { it + "!" }) 

prega di notare che questo approccio consente solo di parallelizzare l'operazione map e non influenza alcun bit a valle. Per esempio. il filter nel primo esempio verrebbe eseguito a thread singolo. Tuttavia, in molti casi, solo la trasformazione dei dati (ad esempio map) richiede la parallelizzazione. Inoltre, sarebbe semplice estendere l'approccio dall'alto ad altri elementi dell'API di raccolta di Kotlin.

+0

Non vedo come" destination.add (transform (item)) "è thread-safe. Cosa deve impedire a due thread di chiamare "destination.add" allo stesso tempo, rompendo così roba da quando ArrayList.add() non è un'operazione thread-safe? –

+0

Grazie per il suggerimento. Piuttosto [alcuni] (http://stackoverflow.com/questions/2715983/concurrent-threads-adding-to-arraylist-at-same-time-what-happens) la gente pensa che quando si aggiungono elementi dovrebbe andare bene senza sincronizzazione . Tuttavia, l'ho modificato per utilizzare un elenco sincronizzato per migliorare la sicurezza del thread. –

+1

L'ordine di destinazione potrebbe non essere lo stesso dell'elenco originale –

10

A partire da Kotlin 1.1, le operazioni parallele possono anche essere espresse in modo abbastanza elegante in termini di coroutines. Ecco pmap sulle liste:

fun <A, B>List<A>.pmap(f: suspend (A) -> B): List<B> = runBlocking { 
    map { async(CommonPool) { f(it) } }.map { it.await() } 
} 

Nota che coroutine sono ancora una funzione sperimentale.