45

Scala has all sorts sorts of immutable sequences like List, Vector,etc. I have been surprised to find no implementation of immutable indexed sequence backed by a simple array (Vector seems way too complicated for my needs).

  • Is there a design reason for this? I could not find a good explanation on the mailing list.

  • Do you have a recommendation for an immutable indexed sequence that has close to the same performances as an array? I am considering scalaz's ImmutableArray, but it has some issues with scala trunk for example.

Thank you

3
  • How is Vector-too-complicated? You're not supposed to tinker with it's innards. Commented Jan 28, 2011 at 10:04
  • 4
    "I am considering scalaz's ImmutableArray, but it has some issues with scala trunk for example." Then that needs to be fixed :) Commented Jan 28, 2011 at 13:14
  • scalaz has a separate branch for 2.9.x Commented Jan 30, 2011 at 20:48

8 Answers 8

32

You could cast your array into a sequence.

val s: Seq[Int] = Array(1,2,3,4)

The array will be implicitly converted to a WrappedArray. And as the type is Seq, update operations will no longer be available.

Sign up to request clarification or add additional context in comments.

5 Comments

unfortunately, s.asInstanceOf[mutable.WrappedArray[Int]].update(1, 42) works.
@KonstantinPelepelin So?
So it's not immutable, which was requested.
@KonstantinPelepelin You do realise that by casting you're hijacking the type system and all talks about type-safety are pointless from that point?
@Nikita Volkov Downcasting is not disallowed by type system. In Scala idiomatic code one would be hit by x match { case mutable.Seq ... } with the same sad effect.
21

So, let's first make a distinction between interface and class. The interface is an API design, while the class is the implementation of such API.

The interfaces in Scala have the same name and different package to distinguish with regards to immutability: Seq, immutable.Seq, mutable.Seq.

The classes, on the other hand, usually don't share a name. A List is an immutable sequence, while a ListBuffer is a mutable sequence. There are exceptions, like HashSet, but that's just a coincidence with regards to implementation.

Now, and Array is not part of Scala's collection, being a Java class, but its wrapper WrappedArray shows clearly where it would show up: as a mutable class.

The interface implemented by WrappedArray is IndexedSeq, which exists are both mutable and immutable traits.

The immutable.IndexedSeq has a few implementing classes, including the WrappedString. The general use class implementing it, however, is the Vector. That class occupies the same position an Array class would occupy in the mutable side.

Now, there's no more complexity in using a Vector than using an Array, so I don't know why you call it complicated.

Perhaps you think it does too much internally, in which case you'd be wrong. All well designed immutable classes are persistent, because using an immutable collection means creating new copies of it, so they have to be optimized for that, which is exactly what Vector does.

4 Comments

It's a clear explanation, but vector access is at least 4-6x slower than raw array access (10-30x if you're using arrays of primitives since Vector isn't specialized yet and you have a boxing penalty as well). The OP asked for performance. Vector does not deliver "close to the same performance".
@Rex As you well know, it is not possible outside specialization. And even with specialization I think we'll stumble upon cases where there's still a penalty to be paid. It is not Scala's fault that Java has a non-type-erased Array, but no one else is allowed to do so.
Even aside from specialization, Vector is slow to access. My 4-6x penalty is for calling methods on classes stored in WrappedArray vs. Vector.
Using Vector as opposed to Array carries a baggage as proven by Mr Haoyi: lihaoyi.com/post/BenchmarkingScalaCollections.html It matters only after certain sizes of course
12

Mostly because there are no arrays whatsoever in Scala. What you're seeing is java's arrays pimped with a few methods that help them fit into the collection API.

Anything else wouldn't be an array, with it's unique property of not suffering type erasure, or the broken variance. It would just be another type with indexes and values. Scala does have that, it's called IndexedSeq, and if you need to pass it as an array to some 3rd party API then you can just use .toArray

2 Comments

"There are no arrays whatsoever" is highly misleading. There is a ".toArray" method on nearly everything in the collections hierarchy, and that is converted to a plain old Java array and Scala emits identical bytecode to Java when dealing with such arrays.
@rex - exactly, these arrays are a special construct in the JVM, they're not something that scala provides any more than it provides spring context factories. Though it can, of course, use them.
11

Scala 2.13 has added ArraySeq, which is an immutable sequence backed by an array.

Comments

6

Scala 3 now has IArray, an Immutable Array.

It is implemented as an Opaque Type Alias, with no runtime overhead.

Comments

1

The point of the scala Array class is to provide a mechanism to access the abilities of Java arrays (but without Java's awful design decision of allowing arrays to be covariant within its type system). Java arrays are mutable, hence so are those in the scala standard library.

Suppose there were also another class immutable.Array in the library but that the compiler were also to use a Java array as the underlying structure (for efficiency/speed). The following code would then compile and run:

val i = immutable.Array("Hello")
i.asInstanceOf[Array[String]](0) = "Goodbye"
println( i(0) ) //I thought i was immutable :-(

That is, the array would really be mutable.

2 Comments

If we had immutable.Array, we could move what we have now to mutable.Array and have only non-updating methods in a common supertype (analoguously to other collections). Your "counterexample" would not work then.
The immutable array would only need to be backed by an array. You don't need to make the backing collection accessible.
0

The problem with Arrays is that they have a fixed size. There is no operation to add an element to an array, or remove one from it.

You can keep an array that you guess will be long enough as a backing store, "wasting" the memory you're not using, keep track of the last used index, and copy to a larger array if you need the extra space. That copying is O(N) obviously.

Changing a single element is also O(N) as you will need to copy over the entire array. There is no structural sharing, which is the lynchpin of performant functional datastructures.

You could also allocate an extra array for the "overflowing" elements, and somehow keep track of your arrays. At that point you're on your way of re-inventing Vector.

In short, due to their unsuitablility for structural sharing, immutable facades for arrays have terrible runtime performance characteristics for most common operations like adding an element, removing an element, and changing an element.

That only leaves the use-case of a fixed size fixed content data-carrier, and that use-case is relatively rare. Most uses better served with List, Stream or Vector

2 Comments

Your comments are valid for mutable arrays, but do not apply to immutable arrays where the size and contents will not change.
@pndc I don't see how any of my comments are more valid for mutable arrays -- the performance characteristics are the same (and generally terrible, O(n) for adding an element) or better than they are for for immutable arrays -- changing an element is O(1) for a mutable array, not O(n) as it is for an immutable array facade.
0

You can simply use Array[T].toIndexSeq to convert Array[T] to ArraySeq[T], which is of type immutable.IndexedSeq[T]. (after Scala 2.13.0)

scala> val array = Array(0, 1, 2)
array: Array[Int] = Array(0, 1, 2)

scala> array.toIndexedSeq
res0: IndexedSeq[Int] = ArraySeq(0, 1, 2)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.