2015-12-05 14 views
5

Ich habe folgenden Code, indem Sie durch das Java-Stream-API eine Karte aus einer Liste in einer parallelen Weise aufzufüllen versucht:Woher weiß ich, ob Java Stream sammeln (Collectors.toMap) parallelisiert ist?

class NameId {...} 

public class TestStream 
{ 
    static public void main(String[] args) 
    { 
     List<NameId > niList = new ArrayList<>(); 
     niList.add(new NameId ("Alice", "123456")); 
     niList.add(new NameId ("Bob", "223456")); 
     niList.add(new NameId ("Carl", "323456")); 

     Stream<NameId> niStream = niList.parallelStream(); 
     Map<String, String> niMap = niStream.collect(Collectors.toMap(NameId::getName, NameId::getId)); 
    } 
} 

Wie kann ich wissen, wenn die Karte mehr Threads besiedelten verwendet, dh in parallel? Muss ich Collectors.toConcurrentMap anstelle von Collectors.toMap aufrufen? Ist dies ein vernünftiger Weg, um die Bevölkerung einer Karte zu parallelisieren? Woher weiß ich, dass die konkrete Karte die neue niMap unterstützt (z. B. HashMap)?

+0

wobei n die Anzahl der Kern-Prozessor haben , n-1-Thread wird erstellt, um mit Ihrem parallelen Stream zu arbeiten. Sie haben 3 Zeilen in Ihrer Liste, so dass Sie die Leistung wahrscheinlich verringern werden. –

+0

Das Stream-Framework versteckt die Implementierung (parallel oder nicht) von Ihnen absichtlich. Wenn alles richtig gemacht wird, gibt es keine Möglichkeit zu sagen. –

Antwort

2

Vom Javadoc:

Der zurück Collector ist nicht gleichzeitig. Bei Parallelstream-Pipelines funktioniert die Combiner-Funktion, indem die Schlüssel von einer Karte in eine andere verschmelzen, was eine teure Operation sein kann. Wenn es nicht erforderlich ist, dass die Ergebnisse in der Reihenfolge der Zuordnung in die Map eingefügt werden, kann die Verwendung von toConcurrentMap (Function, Function) eine bessere parallele Leistung bieten.

So klingt es wie toConcurrentMap wird die Einsätze parallelisieren.

Die Hintergrundkarte ist standardmäßig HashMap. Es ruft nur die Version toMap, die eine Supplier<M> dauert und übergibt HashMap::new. (Quelle: die Quelle)

2

Woher weiß ich, ob die Karte mit mehreren Threads belegt ist, d. h. parallel?

Es ist schwer zu sagen. Wenn Ihr Code überraschend langsam geht, könnte es sein, weil Sie versuchen, mehrere Threads zu verwenden.

Muss ich Collectors.toConcurrentMap anstelle von Collectors.toMap aufrufen?

Dies würde helfen, die Parallele effizienter zu machen oder einen anderen Weg zu nehmen, ein bisschen weniger ineffizient.

Ist dies ein vernünftiger Weg, um die Population einer Karte zu parallelisieren?

Sie können es tun, wie Sie vorschlagen jedoch sollten Sie beachten, dass die Kosten für einen neuen Thread zu starten, ist viel teurer als alles, was Sie hier so tun, indem noch ein Thread wird es langsam viel nach unten.

Woher weiß ich, dass die konkrete Karte die neue niMap unterstützt (z. B. HashMap)?

Die Dokumentation sagt, dass Sie nicht sicher wissen können. Das letzte Mal, als ich toMap überprüfte, benutzte HashMap und groupingBy benutzte LinkedHashMap, aber Sie können nicht annehmen, dass es irgendeine bestimmte Karte ist.

+0

Würde Ihnen niMap.getClass nicht sagen, welche Karte verwendet wird? –

+1

@ Jean-FrançoisSavard Ja, aber es könnte eine andere Implementierung zwischen Updates von Java oder in der Theorie sein, je nachdem, wie Sie es genannt haben. d. h., wenn Sie keine Objekte an eine leereMap() übergeben, oder wenn es sich um eine SingletonMap() handelt, können Sie nicht einmal annehmen, dass es veränderbar ist. –

+0

Wann hat 'groupingBy' LinkedHashMap verwendet? Ich habe das nie gesehen. –

1

Sie können toConcurrentMap für sequentiellen Stream und toMap für parallelen Stream verwenden.Der Unterschied ist,

  • toConcurrentMap() in der Regel schneller zum parallelen Strom als für sequentiellen Strom
  • toMap() ist in der Regel schneller zum sequentiellen Strom als für den Parallelstrom

Wenn Sie nicht wissen, wo Ihr Strom kam aus und wollen es schneller machen in beiden Fällen können Sie so schreiben:

Map<String, String> niMap = niStream.collect(
    niStream.isParallel() ? 
     Collectors.toConcurrentMap(NameId::getName, NameId::getId) : 
     Collectors.toMap(NameId::getName, NameId::getId) 
); 

Der Unterschied ist, dass toConcurrentMap() ein CONCURRENT Kollektor ist, was bedeutet, dass die gleichzeitige Datenstruktur verwendet wird (ConcurrentHashMap in der aktuellen Implementierung), die gleichzeitig von verschiedenen Threads bevölkert werden kann. Für sequentiellen Stream fügt dies unnötigen Overhead hinzu, aber für parallelen Stream ist es schneller als toMap() wie in toMap() Fall getrennte nicht gleichzeitige Map-Instanzen für jeden parallelen Thread erstellt werden, dann diese Maps zusammengeführt werden, die für große Maps nicht sehr schnell ist .

Hinweis, dass meine StreamEx Bibliothek, die Standard-Stream-API verbessert fügt eine toMap() Methode, die für den Parallelstrom und nicht gleichzeitige Sammlung für den sequentiellen eine gleichzeitige Sammlung verwendet:

Map<String, String> niMap = StreamEx.of(niStream) 
         .toMap(NameId::getName, NameId::getId); 
Verwandte Themen