62

Whilst working through the Scala exercises on Iterables, I encountered the following strange behaviour:

val xs = Set(5,4,3,2,1)
val ys = Set(1,2,3,4,5)
xs sameElements ys       // true

val xs = Set(3,2,1)
val ys = Set(1,2,3)
xs sameElements ys       // false - WAT?!

Surely these Sets have the same elements, and should ignore ordering; and why does this work as expected only for the larger set?

DNA
  • 42,007
  • 12
  • 107
  • 146
  • 2
    Use `==` to test for equality irrespective of order and repetitions. –  Mar 12 '15 at 11:29
  • That's a good point for comparing `Set`s with one another, although I just noticed that `List(1,2,3) == Vector(1,2,3)` but `List(1,2,3) != Set(1,2,3)` and `List(1,2,3) != Array(1,2,3)` which is another potential minefield! – DNA Mar 12 '15 at 11:52
  • And as Paul Draper points out in the comments below, `Array(1,2,3) != Array(1,2,3)` !! – DNA May 16 '16 at 22:12

1 Answers1

108

The Scala collections library provides specialised implementations for Sets of fewer than 5 values (see the source). The iterators for these implementations return elements in the order in which they were added, rather than the consistent, hash-based ordering used for larger Sets.

Furthermore, sameElements (scaladoc) is defined on Iterables (it is implemented in IterableLike - see the source); it returns true only if the iterators return the same elements in the same order.

So although Set(1,2,3) and Set(3,2,1) ought to be equivalent, their iterators are different, therefore sameElements returns false.

This behaviour is surprising, and arguably a bug since it violates the mathematical expectations for a Set (but only for certain sizes of Set!).

As I.K. points out in the comments, == works fine if you are just comparing Sets with one another, i.e. Set(1,2,3) == Set(3,2,1). However, sameElements is more general in that it can compare the elements of any two iterables. For example, List(1, 2, 3) == Array(1, 2, 3) is false, but List(1, 2, 3) sameElements Array(1, 2, 3) is true.

More generally, equality can be confusing - note that:

List(1,2,3) == Vector(1,2,3)
List(1,2,3) != Set(1,2,3)
List(1,2,3) != Array(1,2,3)      
Array(1,2,3) != Array(1,2,3)

I have submitted a fix for the Scala exercises that explains the sameElements problem.

DNA
  • 42,007
  • 12
  • 107
  • 146
  • By the name of the method, I would expect it to disregard order regardless of the collection type. E.g. I would expect `List(1,2,3) sameElements List(3,2,1)` to evaluate to `true`, because both _contain the same elements_. If order matters, then what's the intended difference between `sameElements` and `==`? – Zoltán Mar 12 '15 at 11:40
  • 4
    Great find...In a big project something like this can drive anyone mad. Have you raised a ticket for this in the official scala issues. – sarveshseri Mar 12 '15 at 11:40
  • 2
    I love scala so much but bugs like this are so disheartening. Good find. – Ben Reich Mar 12 '15 at 12:34
  • @Sarvesh_Kumar_Singh It's discussed [here](https://groups.google.com/forum/#!topic/scala-debate/je4vZbR3WbA) on the scala-debate list, but no ticket is mentioned there... – DNA Mar 12 '15 at 12:54
  • https://issues.scala-lang.org/browse/SI-888 is closely related, but marked as Won't Fix (other than updating the documentation for sameElements) – DNA Mar 12 '15 at 12:59
  • 2
    @Zoltán, I completely agree. On difference: Scala does not override `==` for Arrays (or at least not anymore). So `==` and `sameElements` can produce different answers for arrays. – Paul Draper May 16 '16 at 21:05
  • with Scala 2.13.0, IterableOnce defines sameElements as deprecated (in Iterable, AbstractIterable, AbstractSet, ImmutableKeySet, immutablle.Map, and others): "Use .iterator.sameElements instead" – Hartmut Pfarr Jan 31 '21 at 19:48
  • "Use == to test for equality irrespective of order and repetitions" as M.K. stated above. Problem solved for Sets. – Hartmut Pfarr Jan 31 '21 at 20:23