Queue implementation in Swift language

Aside from the bugs, there are a couple of things about your implementation that you probably want to change to make it more Swift-like. One is it looks like you're replicating the Java names like poll and offer – these names are (IMHO) a little strange, and partly related to needing to have two functions, an exception-throwing version and a non-exception version. Since Swift doesn't have exceptions, you can probably just name them using the conventional names other Swift collections use, like append.

The other issue is that your implementation incorporates traversing the queue into the queue itself. It's better to do this kind of traversal outside the collection than mix the two. Swift collections do this with indexes.

Here's a possible Swift-like queue implementation. First, the node and base queue definition:

// singly rather than doubly linked list implementation
// private, as users of Queue never use this directly
private final class QueueNode<T> {
    // note, not optional – every node has a value
    var value: T
    // but the last node doesn't have a next
    var next: QueueNode<T>? = nil

    init(value: T) { self.value = value }
}

// Ideally, Queue would be a struct with value semantics but 
// I'll leave that for now
public final class Queue<T> {
    // note, these are both optionals, to handle
    // an empty queue
    private var head: QueueNode<T>? = nil
    private var tail: QueueNode<T>? = nil

    public init() { }
}

Then, extend with an append and dequeue method:

extension Queue {
    // append is the standard name in Swift for this operation
    public func append(newElement: T) {
        let oldTail = tail
        self.tail = QueueNode(value: newElement)
        if  head == nil { head = tail }
        else { oldTail?.next = self.tail }
    }

    public func dequeue() -> T? {
        if let head = self.head {
            self.head = head.next
            if head.next == nil { tail = nil }
            return head.value
        }
        else {
            return nil
        }
    }
}

At this point, you're almost done if all you want to do is add and remove. To add traversal, first create an index type, which is a simple wrapper on the node type:

public struct QueueIndex<T>: ForwardIndexType {
    private let node: QueueNode<T>?
    public func successor() -> QueueIndex<T> {
        return QueueIndex(node: node?.next)
    }
}

public func ==<T>(lhs: QueueIndex<T>, rhs: QueueIndex<T>) -> Bool {
    return lhs.node === rhs.node
}

Then, use this index to conform to MutableCollectionType:

extension Queue: MutableCollectionType {
    public typealias Index = QueueIndex<T>
    public var startIndex: Index { return Index(node: head) }
    public var endIndex: Index { return Index(node: nil) }

    public subscript(idx: Index) -> T {
        get {
            precondition(idx.node != nil, "Attempt to subscript out of bounds")
            return idx.node!.value
        }
        set(newValue) {
            precondition(idx.node != nil, "Attempt to subscript out of bounds")
            idx.node!.value = newValue
        }
    }

    typealias Generator = IndexingGenerator<Queue>
    public func generate() -> Generator {
        return Generator(self)
    }
}

From conforming to collection type, you get a whole load of stuff for free:

var q = Queue<String>()
q.append("one")
q.append("two")

for x in q {
    println(x)
}

isEmpty(q) // returns false
first(q)   // returns Optional("one")
count(q)   // returns 2
",".join(q)  // returns "one,two"
let x = find(q, "two")  // returns index of second entry
let counts = map(q) { count($0) }  // returns [3,3]

Finally, there's 3 more protocols that are good to conform to: ExtensibleCollectionType, Printable and ArrayLiteralConvertible:

// init() and append() requirements are already covered
extension Queue: ExtensibleCollectionType {
    public func reserveCapacity(n: Index.Distance) {
        // do nothing
    }

    public func extend<S : SequenceType where S.Generator.Element == T>
      (newElements: S) {
        for x in newElements {
            self.append(x)
        }
    }
}

extension Queue: ArrayLiteralConvertible {
    public convenience init(arrayLiteral elements: T...) {
        self.init()
        // conformance to ExtensibleCollectionType makes this easy
        self.extend(elements)
    }
}

extension Queue: Printable {
    // pretty easy given conformance to CollectionType
    public var description: String {
        return "[" + ", ".join(map(self,toString)) + "]"
    }
}

These mean you can now create queues as easily arrays or sets:

var q: Queue = [1,2,3]
println(q)  // prints [1, 2, 3]

There are a lot of little issues regarding the internal consistency of your model:

  1. When you first instantiate a new Queue, you are initializing head, tail and current to three different Node objects (even though nothing's been queued yet!). That doesn't make sense. Personally, I'd be inclined to make those three properties optional and leave them as nil until you start enqueuing stuff.

    By the way, when you start using optionals for these properties, many of the other methods are simplified.

  2. It looks like you're trying to implement a doubly linked list. So, when you dequeue, you need to not only update the Queue properties, but you also need to update the next pointer for the next item that will be dequeued (because it still will be pointing to that item you already dequeued). You don't want your linked list maintaining references to objects that have been dequeued and should be removed.

  3. When you dequeue the last item, you really should be clearing out head and tail references.

  4. You're implementing a doubly linked list, without regard to the object ownership model. Thus, as soon as you have more than one item in your list, you've got a strong reference cycle between nodes and if not remedied, this will leak if there are still objects in the queue when the queue, itself, is deallocated. Consider making one of the references weak or unowned.

  5. I'd suggest keeping this simple (just enqueue and dequeue). The concept of poll and offer may make sense in terms of an arbitrary linked list, but not in the context of a queue. The implementations of poll and offer are also incorrect (e.g. poll calls deQueue which removes the tail, but the object you return is the head!), but I presume you'd just remove these functions altogether. Likewise, I do not understand the intent of current in the context of a queue.

  6. I'd suggest you make Queue and Node conform to Printable. It will simplify your debugging process.


The following is code of a playground consisting of a queue implemented with an array and a queue implemented with nodes. There are substantial performance differences between the two but if you going to be iterating through a queue you might want to use one with an array.

import UIKit // for NSDate() used in testing)

// QUEUE WITH ARRAY IMPLEMENTATION (For ease of adaptibility, slow enque, faster deque):

struct QueueArray<T> {
    private var items = [T]()

    mutating func enQueue(item: T) {
        items.append(item)
    }

    mutating func deQueue() -> T? {
        return items.removeFirst()
    }

    func isEmpty() -> Bool {
        return items.isEmpty
    }

    func peek() -> T? {
        return items.first
    }
}

// QUEUE WITH NODE IMPLEMENTATION (For performance, if all you need is a queue this is it):

class QNode<T> {
    var value: T
    var next: QNode?

    init(item:T) {
        value = item
    }
}

struct Queue<T> {
    private var top: QNode<T>!
    private var bottom: QNode<T>!

    init() {
        top = nil
        bottom = nil
    }

    mutating func enQueue(item: T) {

        let newNode:QNode<T> = QNode(item: item)

        if top == nil {
            top = newNode
            bottom = top
            return
        }

        bottom.next = newNode
        bottom = newNode
    }

    mutating func deQueue() -> T? {

        let topItem: T? = top?.value
        if topItem == nil {
            return nil
        }

        if let nextItem = top.next {
            top = nextItem
        } else {
            top = nil
            bottom = nil
        }

        return topItem
    }

    func isEmpty() -> Bool {

        return top == nil ? true : false
    }

    func peek() -> T? {
        return top?.value
    }
}



// QUEUE NODES TEST

let testAmount = 100

var queueNodes = Queue<Int>()

let queueNodesEnqueStart = NSDate()

for i in 0...testAmount {
    queueNodes.enQueue(i)
}

let queueNodesEnqueEnd = NSDate()

while !queueNodes.isEmpty() {
    queueNodes.deQueue()
}

let queueNodesDequeEnd = NSDate()

// QUEUE ARRAY TEST

var queueArray = QueueArray<Int>()

let queueArrayEnqueStart = NSDate()

for i in 0...testAmount {
    queueArray.enQueue(i)
}

let queueArrayEnqueEnd = NSDate()

while !queueArray.isEmpty() {
    queueArray.deQueue()
}

let queueArrayDequeEnd = NSDate()

// QUEUE NODES RESULT:

print("queueEnqueDuration: \(queueNodesEnqueEnd.timeIntervalSinceDate(queueNodesEnqueStart)), Deque: \(queueNodesDequeEnd.timeIntervalSinceDate(queueNodesEnqueEnd))")

// QUEUE ARRAY RESULT:

print("queueArrayEnqueDuration: \(queueArrayEnqueEnd.timeIntervalSinceDate(queueArrayEnqueStart)), Deque: \(queueArrayDequeEnd.timeIntervalSinceDate(queueArrayEnqueEnd))")

Tags:

Ios

Queue

Swift