Scala中的可变链接列表

时间:2019-03-16 07:00:31

标签: scala

斯卡拉新手。我正在尝试在Scala中实现可变链表。 (存在一个用于执行相同操作的库类-我只是在尝试将其作为学习练习)。到目前为止,我仅支持add()操作。这是我的目的:

class MutableList[T] {

  class Node[T](val value: T) {
    var next: Node[T] = null
    def hasNext = (next != null)
  }

  var first, last: Node[T] = null
  var size = 0

  def add(value: T) = {
    val newNode = new Node[T](value)
    if (first == null) {
      assert(size == 0)
      first = newNode
    } else {
      last.next = newNode
    }
    last = newNode
    size += 1
  }

  override def toString = {
    def getValues() = {
      var current = first
      for (i <- 1 to size) yield {
        val valStr = current.value.toString
        current = current.next
        valStr
      }
    }
    getValues().mkString(",")
  }
}

我知道,可变数据结构并不是在Scala中使用/实现的最好方法。但是我只是在做实验,并且想知道是否有更实用的Scala编写方式?

编辑:

感谢大家的所有评论。我试图删除null并使用更多本机Scala构造。欢迎发表评论。

class MutableLinkedList[T] {

  private type Node = MutableLinkedList[T]#MutableListNode

  class MutableListNode(val value: T) {
    var next: Option[Node] = None
  }

  private var first, last: Option[Node] = None
  private var _size = 0
  def size = _size

  def add(value: T) = {
    val newNode = Some(new MutableListNode(value))
    first match {
      case None => {
        assert(_size == 0)
        first = newNode
      }
      case Some(x) => {
        assert(last.isDefined)
        last.get.next = newNode
      }
    }
    last = newNode
    _size += 1
  }

  def head: Option[T] = {
    first match {
      case None => None
      case Some(x) => Some(x.value)
    }
  }

  def tail: Option[MutableLinkedList[T]] = {
    first match {
      case None => None
      case Some(x) => {
        var l = new MutableLinkedList[T]
        l.first = this.first.get.next
        l.last = this.last
        l._size = this.size - 1
        Some(l)
      }
    }
  }

  def exists(value: T): Boolean = {
    var current = first
    var foundIt = false
    while(current.isDefined && !foundIt) {
      if(current.get.value == value)
        foundIt = true
      current = current.get.next
    }
    foundIt
  }

  def delete(value: T): Boolean = {
    var previous: Option[Node] = None
    var current = first
    var deleted = false
    while(current.isDefined && !deleted) {
      if(current.get.value == value) {
        if(!previous.isDefined)
          first = current.get.next
        else
          previous.get.next = current.get.next
        _size -= 1
        deleted = true
      }
      previous = current
      current = current.get.next
    }
    deleted
  }

  override def toString = {
    var current = first
    var output = ""
    while(current.isDefined) {
      output += current.get.value.toString
      current = current.get.next
      if(current.isDefined)
        output += ","
    }
    output
  }
}

2 个答案:

答案 0 :(得分:3)

首先,您实际上并不需要两个地方的type参数:

class MutableList[T] { //type parameter T

  class Node[T](val value: T) {...
          //^^^ a new type parameter T, shadowing the 1st

我会保留第一名,放下第二名,但是它可能会发生任何一种变化。

避免使用null的常见方法是使用Option

class Node(val value: T) {
  var next: Option[Node] = None //no hasNext needed
}

设置好后,您的toString可以简单地从firstiterate开始,直到用尽非None节点为止。

override def toString :String =
  Stream.iterate(Option(first))(_.flatMap(_.next))
        .takeWhile(_.nonEmpty)
        .map(_.get.value.toString)
        .mkString(",")

可能有更多方法可以消除代码中对null的使用,但是到目前为止,您尚未实现任何需要last(或size)的方法,因此很难推荐合适的解决方案。

我必须同意Brian McCutchon的评论,这可能更适合Code Review

答案 1 :(得分:1)

此功能的版本将是不可变的。如果在列表的开头(O(1)时间)追加,则实现非常容易且高效。删除将花费O(i)时间,其中i是删除发生的索引。

如果您不需要在列表的中间或结尾快速添加内容,并且不需要在固定时间内知道大小,那么坚持使用不可变版本是个不错的选择。该实现甚至更加容易,您可以在collection.immutable.List中阅读它。

here描述了一个没有所有标准集合库绒毛的更简单的实现。