Array vs ArraySeq comparison
From the scala-lang.org forum:
Array[T] - Benefits: Native, fast - Limitations: Few methods (only apply, update, length), need to know T at compile-time, because Java bytecode represents (char[] different from int[] different from Object[])
ArraySeq[T] (the class formerly known as GenericArray[T]): - Benefits: Still backed by a native Array, don't need to know anything about T at compile-time (new ArraySeq[T] "just works", even if nothing is known about T), full suite of SeqLike methods, subtype of Seq[T] - Limitations: It's backed by an Array[AnyRef], regardless of what T is (if T is primitive, then elements will be boxed/unboxed on their way in or out of the backing Array)
ArraySeq[Any] is much faster than Array[Any] when handling primitives. In any code you have Array[T], where T isn't <: AnyRef, you'll get faster performance out of ArraySeq.
There are actually four different classes you could choose from to get mutable array-like functionality.
Array + ArrayOps
WrappedArray
ArraySeq
ArrayBuffer
Array
is a plain old Java array. It is by far the best way to go for low-level access to arrays of primitives. There's no overhead. Also it can act like the Scala collections thanks to implicit conversion to ArrayOps
, which grabs the underlying array, applies the appropriate method, and, if appropriate, returns a new array. But since ArrayOps
is not specialized for primitives, it's slow (as slow as boxing/unboxing always is).
WrappedArray
is a plain old Java array, but wrapped in all of Scala's collection goodies. The difference between it and ArrayOps
is that WrappedArray
returns another WrappedArray
--so at least you don't have the overhead of having to re-ArrayOps
your Java primitive array over and over again for each operation. It's good to use when you are doing a lot of interop with Java and you need to pass in plain old Java arrays, but on the Scala side you need to manipulate them conveniently.
ArraySeq
stores its data in a plain old Java array, but it no longer stores arrays of primitives; everything is an array of objects. This means that primitives get boxed on the way in. That's actually convenient if you want to use the primitives many times; since you've got boxed copies stored, you only have to unbox them, not box and unbox them on every generic operation.
ArrayBuffer
acts like an array, but you can add and remove elements from it. If you're going to go all the way to ArraySeq
, why not have the added flexibility of changing length while you're at it?
Array
is a direct representation of Java's Array
, and uses the exact same bytecode on the JVM.
The advantage of Array is that it's the only collection type on the JVM to not undergo type erasure, Arrays are also able to directly hold primitives without boxing, this can make them very fast under some circumstances.
Plus, you get Java's messed up array covariance behaviour. (If you pass e.g. an Array[Int]
to some Java class it can be assigned to a variable of type Array[Object]
which will then throw an ArrayStoreException
on trying to add anything that isn't an int
.)
ArraySeq
is rarely used nowadays, it's more of a historic artifact from older versions of Scala that treated arrays differently. Seeing as you have to deal with boxing anyway, you're almost certain to find that another collection type is a better fit for your requirements.
Otherwise... Arrays have exactly the same API as ArraySeq, thanks to an implicit conversion from Array
to ArrayOps
.
Unless you have a specific need for the unique properties of arrays, try to avoid them too. See This Talk at around 19:30 or This Article for an idea of the sort of problems that Arrays can introduce.
After watching that video, it's interesting to note that Scala uses Seq
for varargs :)