9

I'm using scala 2.9.1, when I try this code:

import scala.collection.mutable
val a = mutable.Set(1,2,3,4,7,0,98,9,8)
a.foreach(x => { println(x); a.remove(x) })

the result was something like

0
98
2
1
4
3
8

which did not list all the elements of a. After this, a becomes Set(9, 7) instead of empty set. It looks very weird to me, is it a bug or we just cannot modify the set itself when doing foreach?

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
user1923692
  • 111
  • 3
  • 4
    Why the downvote? I think it's a very valid question for someone new to Iterators and how they work – Plasty Grove Dec 22 '12 at 15:06
  • 4
    I have not seen anything like that, but you could accomplish a similar result with `a.flatMap ( x => { println(x); if(cond) None else Some(x) })` which will return an new set with the `None` items removed. – jcern Dec 22 '12 at 15:22
  • Not only is it a valid question, it duplicates http://stackoverflow.com/questions/2803085/iterators-for-mutable-collections-in-scala and http://stackoverflow.com/questions/4417994/scala-remove-in-place-all-elements-of-a-listbuffer-that-meet-a-condition and http://stackoverflow.com/questions/2500548/what-is-the-proper-way-to-remove-elements-from-a-scala-mutable-map-using-a-predi – som-snytt Dec 24 '12 at 16:50

1 Answers1

10

You may not modify a collection while traversing or iterating over it.

This is the same in Scala as it is in Java (and most other programming languages/libraries). Except that in Java, the Iterator class provides a remove method that can be used instead of the collection's remove method to remove elements while iterating using that Iterator (but will invalidate any other iterators of that collection that might be in use). Scala Iterators provide no such method.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • `foreach` is "traversing" not "iterating". – Rex Kerr Dec 22 '12 at 15:12
  • @sepp2k nice comment, but it didn't say nothing about reason why it is outputting such a strange result (why it gives this particular result) – xhudik Dec 22 '12 at 18:43
  • 1
    @xhudik It gives an unexpected result because it does something forbidden that causes unpredictable behavior. The specific result occurs because of implementation details of the HashMap class that I don't know. One thing I could imagine is that there's a hash collision and deleting one of the conflicting elements causes other elements to move into the already-iterated part of the array that backs up the hash map. So the iteration wouldn't get to those items. – sepp2k Dec 22 '12 at 20:57
  • 1
    @sepp2k,@user1923692 - if so, user1923692 is right - it is a bug. Unpredictable behavior(i.e. "it does something forbidden" - as you wrote) should be stopped by emitting an error or exception - correct? – xhudik Dec 23 '12 at 09:23
  • @xhudik No. I mean that is what you'd expect from well-behaved languages like Java and Scala, but modifying a collection while iterating over it is explicitly documented to leave the collection in an unspecified state (at least in the Java API - I don't know where it says so in the Scala API). So if anything it's a shortcoming of the API design, not a bug in the implementation. – sepp2k Dec 23 '12 at 10:35
  • @sepp2k - ok, I redesign my comment: it is not a problem that scala modify the collection into unpredictable state. But the compiler should emit some warning (or exception) in order to inform a programmer that something was done wrong. Better? – xhudik Dec 23 '12 at 12:59
  • @xhudik The compiler can't possibly detect that - at least not with 100% accuracy and even an acceptably accurate heuristic would be a very challenging task. – sepp2k Dec 23 '12 at 13:05
  • hmmm strange, so there is no way how to inform a programmer that his result might be corrupted in such situations? Unfortunately, I'm not familiar with compilers but during code optimization step (http://en.wikipedia.org/wiki/Compiler ) it should be detectable - i really don't know... – xhudik Dec 23 '12 at 13:46