我们如何在Swift语言中创建类似于Objective-C中的NSSet
和NSMutableSet
的唯一对象列表。
从Swift 1.2(Xcode 6.3 beta)开始,Swift拥有了本地的集合类型。
根据发布说明:
新增了一个
Set
数据结构,它提供了一个独特元素的通用集合,并具有完整的值语义。它与NSSet
相连,提供类似于Array
和Dictionary
的功能。
以下是一些简单的使用示例:
// Create set from array literal:
var set = Set([1, 2, 3, 2, 1])
// Add single elements:
set.insert(4)
set.insert(3)
// Add multiple elements:
set.unionInPlace([ 4, 5, 6 ])
// Swift 3: set.formUnion([ 4, 5, 6 ])
// Remove single element:
set.remove(2)
// Remove multiple elements:
set.subtractInPlace([ 6, 7 ])
// Swift 3: set.subtract([ 6, 7 ])
print(set) // [5, 3, 1, 4]
// Test membership:
if set.contains(5) {
print("yes")
}
但是还有更多的方法可用。
NSMutableSet
,通过addObject
、removeObject
和allObject
方法来实现。原生的Set
以后也是一个不错的选择。 - superarts.org您可以在Swift中使用任何Objective-C类:
var set = NSMutableSet()
set.addObject(foo)
AnyObject
。 - David MolesSwift没有集合的概念。在Swift中使用NSMutableSet
可能比使用保存虚拟值的Dictionary
更慢。您可以这样做:
var mySet: Dictionary<String, Boolean> = [:]
mySet["something"]= 1
然后只需迭代键。
我已经构建了一个类似于内置的Array
和Dictionary
的广泛的Set
类型 - 这里有两篇博客文章和一个GitHub存储库:
extension Array where Element: Hashable {
var setValue: Set<Element> {
return Set<Element>(self)
}
}
let numbers = [1,2,3,4,5,6,7,8,9,0,0,9,8,7]
let uniqueNumbers = numbers.setValue // {0, 2, 4, 9, 5, 6, 7, 3, 1, 8}
let names = ["John","Mary","Steve","Mary"]
let uniqueNames = names.setValue // {"John", "Mary", "Steve"}
我认为使用带有内部字典的结构体可能是一个不错的选择。我刚开始使用它,所以尚未完全实现,而且性能也不确定。
struct Set<T : Hashable>
{
var _items : Dictionary<T, Bool> = [:]
mutating func add(newItem : T) {
_items[newItem] = true
}
mutating func remove(newItem : T) {
_items[newItem] = nil
}
func contains(item: T) -> Bool {
if _items.indexForKey(item) != nil { return true } else { return false }
}
var items : [T] { get { return [T](_items.keys) } }
var count : Int { get { return _items.count } }
}
实际上,您可以很容易地创建一个Set对象(与GoZoner相反,它具有内置的contains方法):
class Set<T : Equatable> {
var items : T[] = []
func add(item : T) {
if !contains(items, {$0 == item}) {
items += item
}
}
}
你可能甚至想声明自定义运算符:
@assignment @infix func += <T : Equatable> (inout set : Set<T>, items : T[]) -> Set<T> {
for item in items {
set.add(item)
}
return set
}
在这种情况下,关键因素是如何比较对象以及哪些类型的对象放入Set中。使用Swift字典作为Set对象的键可能会有问题,基于键类型(String、Int、Double、Bool、无值枚举或可哈希)的限制。
如果您能定义一个哈希函数,则可以使用字典。如果对象是可排序的,那么可以定义一棵树。如果对象只能通过 == 进行比较
,则需要遍历Set元素以检测是否存在预先存在的对象。
// When T is only Equatable
class Set<T: Equatable> {
var items = Array<T>()
func hasItem (that: T) {
// No builtin Array method of hasItem...
// because comparison is undefined in builtin Array
for this: T in items {
if (this == that) {
return true
}
}
return false
}
func insert (that: T) {
if (!hasItem (that))
items.append (that)
}
}
上面是构建Swift Set的示例;该示例使用的对象仅为Equatable,这虽然是常见情况,但并不一定会导致高效的Set实现(O(N)搜索复杂度-上述是一个示例)。
protocol Equatable { func ==(lhs: Self, rhs: Self) -> Bool }
,而Comparable是protocol Comparable : Equatable { func <=(lhs: Self, rhs: Self) -> Bool func >=(lhs: Self, rhs: Self) -> Bool func >(lhs: Self, rhs: Self) -> Bool }
。 - MarkAureliuspublic func removeDuplicates<C: ExtensibleCollectionType where C.Generator.Element : Equatable>(aCollection: C) -> C {
var container = C()
for element in aCollection {
if !contains(container, element) {
container.append(element)
}
}
return container
}
只需将包含重复元素的数组传递给此函数即可使用它。然后它将返回一个保证唯一性的数组。
如果您喜欢,还可以传递Dictionary
、String
或符合ExtensibleCollectionType
协议的任何内容。
所以我认为用数组创建一个Set是个糟糕的想法 - 时间复杂度是O(n)。
我已经创建了一个使用字典的不错的Set: https://github.com/evilpenguin/Swift-Stuff/blob/master/Set.swift
Set
类型。 - Gary Makin