Lesson 92Advanced Topics

Generic Types

Generic Data Structures

Generic types let you create reusable data structures like Stack, Queue, and LinkedList that work with any type while maintaining type safety.

Common Generic Types

Stack<T>

LIFO - Last In, First Out

push(), pop(), top

Queue<T>

FIFO - First In, First Out

enqueue(), dequeue(), front

LinkedList<T>

Chain of nodes

append(), remove(), traverse

Box<T>

Reference wrapper

Wrap value types in class

Extending Generic Types

extension Stack where Element: Equatable {
    func contains(_ item: Element) -> Bool
}

Add conditional functionality based on type constraints.

Swift Standard Library Generics

  • Array<Element> - Dynamic array
  • Dictionary<Key, Value> - Key-value pairs
  • Set<Element> - Unique collection
  • Optional<Wrapped> - Optional values
  • Result<Success, Failure> - Success or error

Type Alias

typealias IntStack = Stack<Int>
typealias JSON = Dictionary<String, Any>
main.swift
// GENERIC TYPES
// Create reusable data structures that work with any type

// ===== GENERIC STRUCT =====
struct Stack<Element> {
    private var items: [Element] = []

    var isEmpty: Bool { items.isEmpty }
    var count: Int { items.count }
    var top: Element? { items.last }

    mutating func push(_ item: Element) {
        items.append(item)
    }

    mutating func pop() -> Element? {
        return items.popLast()
    }
}

// Usage with different types
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
intStack.push(3)
print(intStack.pop()!)  // 3

var stringStack = Stack<String>()
stringStack.push("Hello")
stringStack.push("World")
print(stringStack.top!)  // World

// ===== GENERIC CLASS =====
class Box<T> {
    var value: T

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

let intBox = Box(42)
let stringBox = Box("Hello")
print(intBox.value)    // 42
print(stringBox.value) // Hello

// ===== GENERIC ENUM =====
enum Result<Success, Failure: Error> {
    case success(Success)
    case failure(Failure)

    func map<NewSuccess>(_ transform: (Success) -> NewSuccess) -> Result<NewSuccess, Failure> {
        switch self {
        case .success(let value):
            return .success(transform(value))
        case .failure(let error):
            return .failure(error)
        }
    }
}

// ===== GENERIC WITH CONSTRAINTS =====
struct SortedArray<Element: Comparable> {
    private var elements: [Element] = []

    var sorted: [Element] { elements }

    mutating func insert(_ element: Element) {
        let index = elements.firstIndex { $0 > element } ?? elements.count
        elements.insert(element, at: index)
    }
}

var sortedNums = SortedArray<Int>()
sortedNums.insert(3)
sortedNums.insert(1)
sortedNums.insert(2)
print(sortedNums.sorted)  // [1, 2, 3]

// ===== GENERIC LINKED LIST =====
class Node<T> {
    var value: T
    var next: Node<T>?

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

class LinkedList<T> {
    var head: Node<T>?

    func append(_ value: T) {
        let newNode = Node(value)
        if head == nil {
            head = newNode
            return
        }
        var current = head
        while current?.next != nil {
            current = current?.next
        }
        current?.next = newNode
    }

    func printAll() {
        var current = head
        while let node = current {
            print(node.value, terminator: " -> ")
            current = node.next
        }
        print("nil")
    }
}

let list = LinkedList<Int>()
list.append(1)
list.append(2)
list.append(3)
list.printAll()  // 1 -> 2 -> 3 -> nil

// ===== GENERIC TYPE ALIAS =====
typealias StringDictionary<T> = Dictionary<String, T>

var scores: StringDictionary<Int> = [:]
scores["Alice"] = 95
scores["Bob"] = 87

// ===== EXTENDING GENERIC TYPES =====
extension Stack {
    func peek() -> Element? {
        return items.last
    }
}

extension Stack where Element: Equatable {
    func contains(_ item: Element) -> Bool {
        return items.contains(item)
    }
}

extension Stack where Element: Numeric {
    func sum() -> Element {
        return items.reduce(0, +)
    }
}

var numStack = Stack<Int>()
numStack.push(1)
numStack.push(2)
numStack.push(3)
print(numStack.sum())  // 6

// ===== GENERIC QUEUE =====
struct Queue<Element> {
    private var elements: [Element] = []

    var isEmpty: Bool { elements.isEmpty }
    var front: Element? { elements.first }

    mutating func enqueue(_ element: Element) {
        elements.append(element)
    }

    mutating func dequeue() -> Element? {
        return isEmpty ? nil : elements.removeFirst()
    }
}

var queue = Queue<String>()
queue.enqueue("First")
queue.enqueue("Second")
print(queue.dequeue()!)  // First

Try It Yourself!

Create a generic BinaryTree<T: Comparable> with insert, search, and inorder traversal!