Reputation: 131
Scala newbie here. I'm trying to implement a mutable linked list in Scala. (there exists a library class for doing the same - I'm just trying this as a learning exercise). So far, I am only supporting the add() operation. Here's my stab at it:
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(",")
}
}
I know that a mutable data structure is not the best thing to use/implement in Scala. But I'm just experimenting and was wondering if there is a more functional Scala way of writing this?
EDIT:
Thanks everyone for all the comments. I have tried to remove null
s and use more native Scala constructs. Comments welcome.
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
}
}
Upvotes: 1
Views: 100
Reputation: 4585
A functional version of this would be immutable. The implementation is very easy a and efficient if you append at the start of the List (O(1) time). Removals will take O(i) time, with i
being the index where the removal occurs.
If you don't specifically need fast addition in the middle or at the end of the list, and you don't need to know the size in constant time, sticking to the immutable version is a good bet. The implementation is even easier, and you can read it in collection.immutable.List
.
A simpler implementation, without all the standard collection library fluff, is described here.
Upvotes: 1
Reputation: 51271
First off, you really don't need or want the type parameter in both locations:
class MutableList[T] { //type parameter T
class Node[T](val value: T) {...
//^^^ a new type parameter T, shadowing the 1st
I'd keep the 1st and drop the 2nd, but it could go either way.
A common means to avoid null
is to use Option
.
class Node(val value: T) {
var next: Option[Node] = None //no hasNext needed
}
With that in place your toString
can simply start at the first
, and iterate
until it runs out of non-None
nodes.
override def toString :String =
Stream.iterate(Option(first))(_.flatMap(_.next))
.takeWhile(_.nonEmpty)
.map(_.get.value.toString)
.mkString(",")
There are likely more ways to eliminate the use of null
in the code but, so far, you haven't implemented any methods that requires last
(or size
) so it's hard to recommend suitable solutions.
I have to agree with Brian McCutchon's comment, this is probably a better fit over at Code Review.
Upvotes: 3