Swift中有类似Java的LinkedHashMap的插入顺序字典吗?

22

是否有一个标准的Swift类是一个字典,但保持键的插入顺序,类似于Java的LinkedHashMap?如果没有,应该如何实现?

2个回答

25

我之前不知道有这样一个问题,但解决它非常有趣(我已经将其放入我的标准库中)。主要就是维护一个字典和一个与其键对应的数组。但像 for (key, value) in odfor key in od.keys 这样的标准操作会按插入顺序而非半随机方式进行迭代。

// OrderedDictionary behaves like a Dictionary except that it maintains
//  the insertion order of the keys, so iteration order matches insertion
//  order.
struct OrderedDictionary<KeyType:Hashable, ValueType> {
    private var _dictionary:Dictionary<KeyType, ValueType>
    private var _keys:Array<KeyType>

    init() {
        _dictionary = [:]
        _keys = []
    }

    init(minimumCapacity:Int) {
        _dictionary = Dictionary<KeyType, ValueType>(minimumCapacity:minimumCapacity)
        _keys = Array<KeyType>()
    }

    init(_ dictionary:Dictionary<KeyType, ValueType>) {
        _dictionary = dictionary
        _keys = map(dictionary.keys) { $0 }
    }

    subscript(key:KeyType) -> ValueType? {
        get {
            return _dictionary[key]
        }
        set {
            if newValue == nil {
                self.removeValueForKey(key)
            }
            else {
                self.updateValue(newValue!, forKey: key)
            }
        }
    }

    mutating func updateValue(value:ValueType, forKey key:KeyType) -> ValueType? {
        let oldValue = _dictionary.updateValue(value, forKey: key)
        if oldValue == nil {
            _keys.append(key)
        }
        return oldValue
    }

    mutating func removeValueForKey(key:KeyType) {
        _keys = _keys.filter { $0 != key }
        _dictionary.removeValueForKey(key)
    }

    mutating func removeAll(keepCapacity:Int) {
        _keys = []
        _dictionary = Dictionary<KeyType,ValueType>(minimumCapacity: keepCapacity)
    }

    var count: Int { get { return _dictionary.count } }

    // keys isn't lazy evaluated because it's just an array anyway
    var keys:[KeyType] { get { return _keys } }

    // values is lazy evaluated because of the dictionary lookup and creating a new array
    var values:GeneratorOf<ValueType> {
        get {
            var index = 0
            return GeneratorOf<ValueType> {
                if index >= self._keys.count {
                    return nil
                }
                else {
                    let key = self._keys[index]
                    index++
                    return self._dictionary[key]
                }
            }
        }
    }
}

extension OrderedDictionary : SequenceType {
    func generate() -> GeneratorOf<(KeyType, ValueType)> {
        var index = 0
        return GeneratorOf<(KeyType, ValueType)> {
            if index >= self._keys.count {
                return nil
            }
            else {
                let key = self._keys[index]
                index++
                return (key, self._dictionary[key]!)
            }
        }
    }
}

func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
    return lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}

func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
    return lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}

2
已点赞,但我希望看到一个理想情况下可以与常规的“Dictionary”语法互换,并扩展了“Dictionary”的类,以便我可以在需要“Dictionary”的其他代码中交替使用它(例如“Alamofire”的“Parameter”枚举)。 - Heath Borders
我不确定这是可能的,Heath。你不能子类化泛型(并保持它们的泛型)。就可替换常规Dictionary语法而言,这应该是一个无需更改即可替换的版本,除了类型之外,当然,由于它不是子类,你不能将其作为字典传递。 - David Berry
不幸的是,将ArrayDictionary结合作为实现有序关联数据结构的手段比使用链式结构(如LinkedHashMap)更慢。首先,平均情况下删除操作的时间复杂度为O(n),而LinkedHashMap的删除操作时间复杂度为O(1) - Alexander
没有争议,@Alexander,然而,如果你只需要存储一些键,特别是如果你很少或从不删除任何键,编写LinkedHashMap或类似的完整实现可能很可能是过早优化的情况。 - David Berry
@DavidBerry 我同意,这就是为什么我会选择使用已经有了完整实现的库,而不是自己编写。 - Alexander
显示剩余5条评论

1

Swift 5 版本:

// OrderedDictionary behaves like a Dictionary except that it maintains
//  the insertion order of the keys, so iteration order matches insertion
//  order.
struct OrderedDictionary<KeyType: Hashable, ValueType> {
    private var _dictionary: Dictionary<KeyType, ValueType>
    private var _keys: Array<KeyType>

    init() {
        _dictionary = [:]
        _keys = []
    }

    init(minimumCapacity: Int) {
        _dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: minimumCapacity)
        _keys = Array<KeyType>()
    }

    init(_ dictionary: Dictionary<KeyType, ValueType>) {
        _dictionary = dictionary
        _keys = dictionary.keys.map { $0 }
    }

    subscript(key: KeyType) -> ValueType? {
        get {
            _dictionary[key]
        }
        set {
            if newValue == nil {
                self.removeValueForKey(key: key)
            } else {
                _ = self.updateValue(value: newValue!, forKey: key)
            }
        }
    }

    mutating func updateValue(value: ValueType, forKey key: KeyType) -> ValueType? {
        let oldValue = _dictionary.updateValue(value, forKey: key)
        if oldValue == nil {
            _keys.append(key)
        }
        return oldValue
    }

    mutating func removeValueForKey(key: KeyType) {
        _keys = _keys.filter {
            $0 != key
        }
        _dictionary.removeValue(forKey: key)
    }

    mutating func removeAll(keepCapacity: Int) {
        _keys = []
        _dictionary = Dictionary<KeyType, ValueType>(minimumCapacity: keepCapacity)
    }

    var count: Int {
        get {
            _dictionary.count
        }
    }

    // keys isn't lazy evaluated because it's just an array anyway
    var keys: [KeyType] {
        get {
            _keys
        }
    }

    var values: Array<ValueType> {
        get {
            _keys.map { _dictionary[$0]! }
        }
    }

    static func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
        lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
    }

    static func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
        lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
    }

}

extension OrderedDictionary: Sequence {

    public func makeIterator() -> OrderedDictionaryIterator<KeyType, ValueType> {
        OrderedDictionaryIterator<KeyType, ValueType>(sequence: _dictionary, keys: _keys, current: 0)
    }

}

struct OrderedDictionaryIterator<KeyType: Hashable, ValueType>: IteratorProtocol {
    let sequence: Dictionary<KeyType, ValueType>
    let keys: Array<KeyType>
    var current = 0

    mutating func next() -> (KeyType, ValueType)? {
        defer { current += 1 }
        guard sequence.count > current else {
            return nil
        }

        let key = keys[current]
        guard let value = sequence[key] else {
            return nil
        }
        return (key, value)
    }

}

我还没有找到让值“惰性”的方法...需要更多的研究。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接