В Scala 2.7.7 вы сразу получаете NullPointer при добавлении нуля:
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...
Используя Scala 2.8.0.RC7, это уже не так.Однако, как заметил Рэндалл , поведение также не совсем соответствует:
<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)
Большая часть работы при добавлении записи выполняется в FlatHashTable Когда выЕсли добавить энтеру, вызывается addEntry
, который выглядит следующим образом:
/** 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
}
null.asInstanceOf[AnyRef]
просто возвращает вам null
, а elemHashCode(elem)<code> returns zero if the elem is <code>null
.Таким образом, в этом случае элемент с нулевым индексом «инициализируется» значением null
(которое уже имело значение), но tableSize увеличивается.Это объясняет результаты, которые видны в REPL.
Если вы вызываете head
на множестве, в конечном счете вызывается итератор в FlatHashTable
, как видно из трассировки стека.Это реализовано следующим образом:
iterator = new Iterator[A] {
private var i = 0
def hasNext: Boolean = {
while (i < table.length && (null == table(i))) i += 1
i < table.length
}
def next(): A =
if (hasNext) { i += 1; table(i - 1).asInstanceOf[A] }
else Iterator.empty.next
}
hasNext
вернет false, поскольку элемент, который мы сохранили в массиве, имеет значение null, поэтому вызов next
вызовет Iterator.empty.next , что вызывает исключение.Поскольку добавление нулевого в HashSet не выполняется последовательно в Scala 2.8, это действительно выглядит как ошибка.Кроме того, как уже отмечалось, это дает еще одну вескую причину не использовать ноль , где это возможно.
Edit Поскольку я не смог найти для этого билет, я добавил это .