Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>In Scala 2.7.7, you get a NullPointer straight away when adding null:</p> <pre><code> scala> val s = new scala.collection.mutable.HashSet[String] s: scala.collection.mutable.HashSet[String] = Set() scala> s += null java.lang.NullPointerException at scala.collection.mutable.FlatHashTable$class.elemHashCode(FlatHashTable.scala:144) at scala.collection.mutable.HashSet.elemHashCode(HashSet.scala:31) at scala.collection.mutable.FlatHashTable$class.addEntry(FlatHashTable.scala:66) at scala.collection.mutable.HashSet.addEntry(HashSet.scala:31) at scala.collection.mutable.HashSet.$plus$eq(HashSet.s... </code></pre> <p>Using Scala 2.8.0.RC7, that is no longer the case. However, as <a href="https://stackoverflow.com/questions/3152167/scala-2-8-mutable-set-null-handling/3152988#3152988">Randall has observed</a>, the behaviour is not entirely consistent either:</p> <pre><code> scala> import scala.collection.mutable.HashSet import scala.collection.mutable.HashSet scala> val s = new HashSet[String] s: scala.collection.mutable.HashSet[String] = Set() scala> s += null res0: s.type = Set() scala> s += null res1: s.type = Set() scala> s.size res2: Int = 2 scala> s.head java.util.NoSuchElementException: next on empty iterator at scala.collection.Iterator$$anon$3.next(Iterator.scala:29) at scala.collection.Iterator$$anon$3.next(Iterator.scala:27) at scala.collection.mutable.FlatHashTable$$anon$1.next(FlatHashTable.scala:176) at scala.collection.IterableLike$class.head(IterableLike.scala:102) at scala.collection.mutable.HashSet.head(HashSet.scala:38) at .(:8) </pre> <p>Most of the work when adding an entry is done in <a href="https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/library/scala/collection/mutable/FlatHashTable.scala" rel="nofollow noreferrer">FlatHashTable</a> When you add an entery, <code>addEntry</code> is invoked, which looks like this:</p> <pre><code> /** The actual hash table. */ @transient protected var table: Array[AnyRef] = new Array(initialCapacity) def addEntry(elem: A) : Boolean = { var h = index(elemHashCode(elem)) var entry = table(h) while (null != entry) { if (entry == elem) return false h = (h + 1) % table.length entry = table(h) } table(h) = elem.asInstanceOf[AnyRef] tableSize = tableSize + 1 if (tableSize >= threshold) growTable() true } </code></pre> <p><code>null.asInstanceOf[AnyRef]</code> just gives you back <code>null</code>, and the <code>elemHashCode(elem)<code> returns zero if the elem is <code>null</code>. So in this case, the element with index zero is 'initialized' with <code>null</code> (which is the value it already had), but the tableSize is increased. This explains the results that are seen in the REPL.</p> <p>If you call <code>head</code> on the Set, ultimately the iterator in <code>FlatHashTable</code> is called, as can be seen from the stacktrace. This is implemented as follows:</p> <pre><code> iterator = new Iterator[A] { private var i = 0 def hasNext: Boolean = { while (i &lt; table.length && (null == table(i))) i += 1 i &lt; table.length } def next(): A = if (hasNext) { i += 1; table(i - 1).asInstanceOf[A] } else Iterator.empty.next } </code></pre> <p><code>hasNext</code> will return false, since the element that we stored in the Array is null, therefore, the call to <code>next</code> will call Iterator.empty.next</code>, which throws an exception. Since adding null</code> to a HashSet is not handled consistently in Scala 2.8, this indeed looks like a bug. Also, as already has been remarked, it provides another good reason to stay away from using null</code> where possible.</p> <p><strong>Edit</strong> Since I couldn't find a ticket for this, I <a href="https://lampsvn.epfl.ch/trac/scala/ticket/3630" rel="nofollow noreferrer">added it</a>.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload