2016-09-29 3 views
1

Scala kann mit verschiedenen Maps für verschiedene Größen von Map-Instanzen instanziieren. Als ErgebnisWie wird eine Scala Map erstellt?

val l = List("a", "b", "c", "d", "e", "f", "g") 

val ms = l.zipWithIndex map { 
    case (e, i) => l.take(i).zipWithIndex.toMap /// toMap 
} 

ms.foreach(m => println(m.getClass)) 

würde emittieren:

class scala.collection.immutable.Map$EmptyMap$ 
class scala.collection.immutable.Map$Map1 
class scala.collection.immutable.Map$Map2 
class scala.collection.immutable.Map$Map3 
class scala.collection.immutable.Map$Map4 
class scala.collection.immutable.HashMap$HashTrieMap 
class scala.collection.immutable.HashMap$HashTrieMap 

(Above-Code wird von toMap erzeugt, aber unter Verwendung apply sehr ähnlich ist.)

Ich finde, dass toMap als

definiert ist
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] = { 
    val b = immutable.Map.newBuilder[T, U] 
    for (x <- self) 
     b += x 

    b.result() 
    } 

Wo der magische Teil wäre result() von Builder, da es schließlich bestimmt, welche Map-Unterklasse instanziiert werden sollte.

Und innen object Map gibt es EmptyMap, Map1, Map2, Map3, Map4 und in object HashMap ist es HashTrieMap; zusätzlich enthalten beide eine implizite MapCanBuildFrom.

Wie Scala-Compiler bestimmt, welche Unterklasse für Map verwendet werden?

(Diese Frage kann mehr über CanBuildFrom sein, zB Seq(1) wäre ein List(1) instanziiert.)

Antwort

5

ich in der scala Bibliothek spähte und es scheint, dass die einzigen Verweise auf diese vier spezifischen impl von Karte in ihrer eigenen sind "aktualisiert" und "-" Methoden. Die Magie beginnt in MutableMapFactory in der Methode newBuilder, in der Map.empty aufgerufen wird, die die EmptyMap-Klasse erzeugt. Dann Aufruf aktualisiert und Hinzufügen von Schlüssel und Wert macht es Map1. Wenn Sie dann die aktualisierte Methode in Map1 erneut aufrufen, kann entweder Map1 oder Map2 und Aufrufmethode zu EmptyMap führen. etc ...

private object EmptyMap extends AbstractMap[Any, Nothing] with Map[Any, Nothing] with Serializable { 
    override def size: Int = 0 
    def get(key: Any): Option[Nothing] = None 
    def iterator: Iterator[(Any, Nothing)] = Iterator.empty 
    override def updated [B1] (key: Any, value: B1): Map[Any, B1] = new Map1(key, value) 
    def + [B1](kv: (Any, B1)): Map[Any, B1] = updated(kv._1, kv._2) 
    def - (key: Any): Map[Any, Nothing] = this 
    } 

    class Map1[A, +B](key1: A, value1: B) extends AbstractMap[A, B] with Map[A, B] with Serializable { 
    override def size = 1 
    def get(key: A): Option[B] = 
     if (key == key1) Some(value1) else None 
    def iterator = Iterator((key1, value1)) 
    override def updated [B1 >: B] (key: A, value: B1): Map[A, B1] = 
     if (key == key1) new Map1(key1, value) 
     else new Map2(key1, value1, key, value) 
    def + [B1 >: B](kv: (A, B1)): Map[A, B1] = updated(kv._1, kv._2) 
    def - (key: A): Map[A, B] = 
     if (key == key1) Map.empty else this 
    override def foreach[U](f: ((A, B)) => U): Unit = { 
     f((key1, value1)) 
    } 
    } 

    class Map2[A, +B](key1: A, value1: B, key2: A, value2: B) extends AbstractMap[A, B] with Map[A, B] with Serializable { 
    override def size = 2 
    def get(key: A): Option[B] = 
     if (key == key1) Some(value1) 
     else if (key == key2) Some(value2) 
     else None 
    def iterator = Iterator((key1, value1), (key2, value2)) 
    override def updated [B1 >: B] (key: A, value: B1): Map[A, B1] = 
     if (key == key1) new Map2(key1, value, key2, value2) 
     else if (key == key2) new Map2(key1, value1, key2, value) 
     else new Map3(key1, value1, key2, value2, key, value) 
    def + [B1 >: B](kv: (A, B1)): Map[A, B1] = updated(kv._1, kv._2) 
    def - (key: A): Map[A, B] = 
     if (key == key1) new Map1(key2, value2) 
     else if (key == key2) new Map1(key1, value1) 
     else this 
    override def foreach[U](f: ((A, B)) => U): Unit = { 
     f((key1, value1)); f((key2, value2)) 
    } 
    } 

    class Map3[A, +B](key1: A, value1: B, key2: A, value2: B, key3: A, value3: B) extends AbstractMap[A, B] with Map[A, B] with Serializable { 
    override def size = 3 
    def get(key: A): Option[B] = 
     if (key == key1) Some(value1) 
     else if (key == key2) Some(value2) 
     else if (key == key3) Some(value3) 
     else None 
    def iterator = Iterator((key1, value1), (key2, value2), (key3, value3)) 
    override def updated [B1 >: B] (key: A, value: B1): Map[A, B1] = 
     if (key == key1)  new Map3(key1, value, key2, value2, key3, value3) 
     else if (key == key2) new Map3(key1, value1, key2, value, key3, value3) 
     else if (key == key3) new Map3(key1, value1, key2, value2, key3, value) 
     else new Map4(key1, value1, key2, value2, key3, value3, key, value) 
    def + [B1 >: B](kv: (A, B1)): Map[A, B1] = updated(kv._1, kv._2) 
    def - (key: A): Map[A, B] = 
     if (key == key1)  new Map2(key2, value2, key3, value3) 
     else if (key == key2) new Map2(key1, value1, key3, value3) 
     else if (key == key3) new Map2(key1, value1, key2, value2) 
     else this 
    override def foreach[U](f: ((A, B)) => U): Unit = { 
     f((key1, value1)); f((key2, value2)); f((key3, value3)) 
    } 
    } 

    class Map4[A, +B](key1: A, value1: B, key2: A, value2: B, key3: A, value3: B, key4: A, value4: B) extends AbstractMap[A, B] with Map[A, B] with Serializable { 
    override def size = 4 
    def get(key: A): Option[B] = 
     if (key == key1) Some(value1) 
     else if (key == key2) Some(value2) 
     else if (key == key3) Some(value3) 
     else if (key == key4) Some(value4) 
     else None 
    def iterator = Iterator((key1, value1), (key2, value2), (key3, value3), (key4, value4)) 
    override def updated [B1 >: B] (key: A, value: B1): Map[A, B1] = 
     if (key == key1)  new Map4(key1, value, key2, value2, key3, value3, key4, value4) 
     else if (key == key2) new Map4(key1, value1, key2, value, key3, value3, key4, value4) 
     else if (key == key3) new Map4(key1, value1, key2, value2, key3, value, key4, value4) 
     else if (key == key4) new Map4(key1, value1, key2, value2, key3, value3, key4, value) 
     else new HashMap + ((key1, value1), (key2, value2), (key3, value3), (key4, value4), (key, value)) 
    def + [B1 >: B](kv: (A, B1)): Map[A, B1] = updated(kv._1, kv._2) 
    def - (key: A): Map[A, B] = 
     if (key == key1)  new Map3(key2, value2, key3, value3, key4, value4) 
     else if (key == key2) new Map3(key1, value1, key3, value3, key4, value4) 
     else if (key == key3) new Map3(key1, value1, key2, value2, key4, value4) 
     else if (key == key4) new Map3(key1, value1, key2, value2, key3, value3) 
     else this 
    override def foreach[U](f: ((A, B)) => U): Unit = { 
     f((key1, value1)); f((key2, value2)); f((key3, value3)); f((key4, value4)) 
    } 
    } 
} 
+0

Ich denke, ich bin klarer. Für 'toMap' sollte der Builder' immaturable.Map.newBuilder' sein. Daher sollte 'empty' derjenige sein, der in' object Map' definiert ist, was 'EmptyMap.asInstanceOf [Map [A, B]]' 'ist. OTOH, '++ =' ruft das zugrundeliegende '+ =' auf, das sich auf "operator +" und "assignment" erstreckt, ersteres würde die entsprechende Methode aufrufen, die in 'object Map' definiert ist. –

Verwandte Themen