Original
Maybe because toString.map uses WrappedString implicitly, and toString.toArray.map uses WrappedArray implicitly to resolve the map .
Let's see the map as defined in the TraversableLike :
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That = { val b = bf(repr) b.sizeHint(this) for (x <- this) b += f(x) b.result }
WrappedString uses the StringBuilder constructor as:
def +=(x: Char): this.type = { append(x); this } def append(x: Any): StringBuilder = { underlying append String.valueOf(x) this }
Calling String.valueOf for Any uses the Java Object.toString in Char instances, perhaps by first inserting a box. These additional operations may cause a difference in speed compared to the supposedly shorter code tracks of the Array constructor.
This assumption, however, would have to measure.
Edit
After the revision, the common point is still there, but I was referring to incorrect implications, as the toDigit methods return an Int sequence (or the like) and not a translated string, as I read incorrectly.
toDigit uses LowPriorityImplicits.fallbackStringCanBuildFrom[T]: CanBuildFrom[String, T, immutable.IndexedSeq[T]] , with T = Int , which simply jumps to the general IndexedSeq constructor.
toDigitFast uses a direct array of implicit type CanBuildFrom[Array[_], T, Array[T]] , which is undeniably faster.
Passing the following CBF to toDigit explicitly makes these two methods equal:
object FastStringToArrayBuild { def canBuildFrom[T : ClassManifest] = new CanBuildFrom[String, T, Array[T]] { private def newBuilder = scala.collection.mutable.ArrayBuilder.make() def apply(from: String) = newBuilder def apply() = newBuilder } }