Scala基于Node的Iterable实现:RandomBag迭代器索引越界问题
解决ImmutableRandomBag的两个异常问题
我来帮你逐一分析并解决遇到的这两个问题:
问题1:调用size时抛出IndexOutOfBoundsException
你的ImmutableRandomBag继承自Iterable[Item],但没有重写size方法。当测试中调用bag.size时,会触发Iterable的默认实现——它会通过遍历迭代器来统计元素数量,而你的迭代器此时存在逻辑问题,直接导致了索引越界异常。
修复方案:
在ImmutableRandomBag类中添加size方法,直接返回构造参数中的size值,既高效又避免依赖迭代器:
class ImmutableRandomBag[Item](maybeNode: Option[Node[Item]], size: Int) extends Iterable[Item]{ // 新增size方法,直接返回构造参数的size override def size: Int = size override def isEmpty: Boolean = size == 0 // ... 其他原有代码 }
问题2:迭代器next方法触发IndexOutOfBoundsException
这个问题的核心是RandomIterator的成员初始化顺序错误,导致container被意外重置为空:
看你的RandomIterator代码,执行顺序是:
- 先执行
first match块,调用random(node)把打乱后的元素赋值给container - 随后执行
var container: Vector[Item] = Vector(),又把container重新设置为空的Vector!
所以当next()调用container(current)时,container是空的,自然抛出索引越界异常。另外,RandomIterator不需要自己的[Item]类型参数,直接使用外部类的Item即可,避免类型不兼容。
修复方案:
调整初始化顺序,把成员变量声明放在match块之前,同时移除冗余的类型参数:
private class RandomIterator(first: Option[Node[Item]]) extends Iterator[Item]{ // 先初始化成员变量 var current: Int = 0 var container: Vector[Item] = Vector() // 再调用random填充数据 first match { case Some(node) => random(node) case None => } override def hasNext: Boolean = current < ImmutableRandomBag.this.size override def next(): Item = { val item = container(current) current += 1 item } // 把辅助方法设为private,避免对外暴露 private def random(first: Node[Item]) = { @tailrec def randomHelper(next: Option[Node[Item]], acc: List[Item]):List[Item]= next match { case None => acc case Some(node) => randomHelper(node.next, node.item::acc) } val items = randomHelper(Some(first), List[Item]()) container = Random.shuffle(items).toVector } }
额外优化建议
在hasNext方法中同时校验container的大小,避免因外部size和container不一致导致的潜在异常:
override def hasNext: Boolean = current < container.size && current < ImmutableRandomBag.this.size
调整后,你的两个测试用例应该就能正常通过了。
内容的提问来源于stack exchange,提问作者vasigorc
相关产品推荐
相关产品推荐

