Why does the scaladoc say HashMap.toArray returns Array[A] instead of Array[(A,B)]?
Asked Answered
M

3

5

I was looking at the definition of toArray for hashmaps :

http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.HashMap

It has

toArray: Array[A]
def toArray[B >: (A, B)](implicit arg0: ClassTag[B]): Array[B]

I don't quite understand this - the first bit says you get an Array[A], but the second part says you get Array[B]? Neither of these are what I expect - Array[(A,B)]

When I check it myself :

scala> val x = scala.collection.mutable.HashMap[String, Int]()
x: scala.collection.mutable.HashMap[String,Int] = Map()

scala> x.put("8", 7)
res0: Option[Int] = None

scala> x foreach println
(8,7)

scala> x.toArray
res2: Array[(String, Int)] = Array((8,7))

why isn't it like toList?

toList: scala.List[(A, B)]
Malaise answered 24/6, 2013 at 14:30 Comment(0)
H
5

The scaladoc has all kinds of subtle bugs. The problem here is that you are seeing the "simplified" version of the method signature (meant as a way to convey the essential part of the signature and hide things such as CanBuildFrom in map/flatMap methods, which are really an implementation detail). The simplification got a little awry here, and does not seem to make much sense. If you click on the "full signature" link, you'll see that the real signature looks like:

def toArray[B >: (A, B)](implicit arg0: ClassTag[B]): Array[B]

In fact this is still wrong, as we certainly cannot have a type B where B >: (A, B). It should be more like :

def toArray[C >: (A, B)](implicit arg0: ClassTag[C]): Array[C]

The problem is that there are actually two Bs: the first one comes from the HashMap class declaration itself (HashMap[A, +B]) while the other one comes from the methods toArray defined in its base class TraversableOnce (def toArray[B >: A](implicit arg0: ClassTag[B]): Array[B]). It just happens that the scaladoc generator failed to dedup the two instances of B

Hamill answered 24/6, 2013 at 14:41 Comment(0)
A
2

The API you see in the the Scaladoc of toArray:

def toArray[B >: (A, B)](implicit arg0: ClassTag[B]): Array[B]

Is equivalent to:

def toArray[C >: (A, B)](implicit arg0: ClassTag[C]): Array[C]

The choice of the type variable B is indeed unfortunate (and maybe even a Scaladoc bug, I'm not sure if you are allowed to write that).

It basically means you'll get an array of the most specific supertype of (A,B) for which a ClassTag is available. The ClassTag is required in order to create the Array.

This basically means that if at compile-time, the-run time type of the Map you are converting is fully known, you will get a Array[(A,B)]. However, if you have up-casted your Map somewhere, the run-time type of the resulting Array will depend on the up-casted type, and not on the runtime type. This is different behavior than toList and due to the JVMs restrictions on how native arrays can be created.

Alage answered 24/6, 2013 at 14:38 Comment(0)
B
1

The Scaladoc is just wrong because it inherits toArray from TraversableOnce, where the type of the collection is A and the return value is B. The Array[A] thing is left over from TraversableOnce where A is whatever TraversableOnce is traversing (in this case, actually (A,B) for a different definition of A and B); and although it fills the (A,B) in properly in the long form, it still uses B as the new return variable instead of a different letter like C.

Kind of confusing! It actually should read

def toArray[C >: (A,B)](...[C]): Array[C]

and the short form should be

toArray: Array[(A,B)]

just like you expect.

Buehler answered 24/6, 2013 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.