2017-04-02 3 views
1

Wenn ich filterKeys oder mapValues auf einem Map verwenden, um einen neuen Map zu erstellen, die Funktionen, die ich in diese Methoden übergeben habe, scheint jedes einzelne Mal, wenn ich die neue Map verwenden ausgeführt werden . Sollten sie nicht nur einmal ausgeführt werden, um den neuen Map zu generieren? In diesem Beispiel sehen Sie, wie die println s mehrmals vorkommen.`filterKeys` und` mapValues` erhalten mehrmals ausgeführt

// printlns happen when creating the map 
scala> val myMap = Map(1 -> 1, 2 -> 2, 3 -> 3).filterKeys { i => 
    println(s"filterKeys$i") 
    i < 5 
    }.mapValues { i => 
    println(s"mapValues$i") 
    i + 1 
    } 
filterKeys1 
mapValues1 
filterKeys2 
mapValues2 
filterKeys3 
mapValues3 
myMap: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4) 

// printlns happen again! 
scala> myMap.toString() 
filterKeys1 
mapValues1 
filterKeys2 
mapValues2 
filterKeys3 
mapValues3 
res29: String = Map(1 -> 2, 2 -> 3, 3 -> 4) 

// and again! 
scala> myMap + (4 -> 5) 
filterKeys1 
mapValues1 
filterKeys2 
mapValues2 
filterKeys3 
mapValues3 
res30: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5) 

würde ich das Verhalten erwartet die gleichen wie .map() zu sein. Die Funktion zur Karte übergeben, wird nur eine für jedes Element ausgeführt und wird nicht erneut ausgeführt werden, wenn die resultierende Karte in der Zukunft verwendet wird:

// printlns happen when creating the map 
scala> val myMap = Map(1 -> 1, 2 -> 2, 3 -> 3).map { i => 
    println(s"map$i") 
    i 
} 
map(1,1) 
map(2,2) 
map(3,3) 
myMap: scala.collection.immutable.Map[Int,Int] = Map(1 -> 1, 2 -> 2, 3 -> 3) 

// printlns not run again :) 
scala> myMap.toString 
res32: String = Map(1 -> 1, 2 -> 2, 3 -> 3) 

Antwort

1

Die scaladocs for mapValues und filterKeys beide erwähnen, dass sie eine resultierende Karte zurück, die Wraps die ursprüngliche Karte, ohne irgendwelche Elemente zu kopieren. Sie können im Quellcode sehen, dass mapValues eine Instanz von MappedValues zurückgibt, die Ihre Funktion für jede wiederholte foreach, iterator und get Aufruf erneut ausführt.

Da dieses Verhalten mit .map() inkonsistent ist, gibt es beim Ticket SI-4776 offen entweder die Methoden umbenennen oder dessen Rückgabetyp ändern, um dies deutlicher zu machen, aber das Ticket für eine lange Zeit offen, also nicht erwarten, diese Verhalten, um sich bald zu ändern.

Um dies zu umgehen, können Sie .view.force nach der Verwendung dieser Methoden anhängen kann eine neue Map zu zwingen, geschaffen werden, oder Sie können einfach .map() statt .mapValues() und .filter() statt .filterKeys() verwenden. Hier ist dein Beispiel mit .view.force angefügt:

// printlns happen when creating the map 
scala> val myMap = Map(1 -> 1, 2 -> 2, 3 -> 3).filterKeys { i => 
    println(s"filterKeys$i") 
    i < 5 
    }.mapValues { i => 
    println(s"mapValues$i") 
    i + 1 
    }.view.force 
filterKeys1 
mapValues1 
filterKeys2 
mapValues2 
filterKeys3 
mapValues3 
myMap: scala.collection.immutable.Map[Int,Int] = Map(1 -> 2, 2 -> 3, 3 -> 4) 

// no printlns when the Map is used again! 
scala> myMap.toString 
res40: String = Map(1 -> 2, 2 -> 3, 3 -> 4) 
+0

Dies wurde auch darüber gebloggt [hier] (http://blog.bruchez.name/2013/02/mapmap-vs-mapmapvalues.html) – theon

Verwandte Themen