我打算使用字典作为我的数据结构,其中键等于圣经中找到的所有单词,值存储整数数组,其中每个整数指向一个经文数组的索引。我的实现看起来像这样:
let verses = [String]() //All verses in the bible
var dict = [String:[Int]]() //Data Structure
func fillDict(){
for verseIndex in 0..<verses.count{
let words = verses[verseIndex].componentsSeparatedByString(" ")
for word in words{
if let indexArray = dict[word]{
var newIndexArray = indexArray
newIndexArray.append(verseIndex)
dict[word] = newIndexArray
}else{
let arr = [verseIndex]
dict[word] = arr
}
}
}
}
填写dict显然非常慢。我正在寻找更快的实现或不同的数据结构,以保证亚线性搜索时间。任何帮助将不胜感激。
答案 0 :(得分:0)
<强> TL; DR: Swift的数组是写时复制的。使用NSMutableArray
避免复制。
Swift的数组是值类型。要将新元素附加到长度为9的数组,需要分配容量为10的数组,复制前9个数组,并将新元素分配给最后一个插槽。这当然需要很多周期。为了演示,让我们稍微修改您的代码并通过Instruments运行它,以了解花了这么长时间的代码:
let bible = try String(contentsOfFile: "King James Bible.txt")
let verses = bible.componentsSeparatedByCharactersInSet(.newlineCharacterSet())
func fillDict1() -> [String: [Int]] {
var dict = [String: [Int]]()
for verseIndex in 0..<verses.count{
let words = verses[verseIndex].componentsSeparatedByString(" ")
for word in words{
if let indexArray = dict[word]{
var newIndexArray = indexArray
newIndexArray.append(verseIndex)
dict[word] = newIndexArray
} else {
let arr = [verseIndex]
dict[word] = arr
}
}
}
return dict
}
fillDict1()
(我在Project Gutenberg使用了King James圣经。我知道verses
阵列没有正确的诗节分解,但这与问题无关手。)
选择产品&gt;个人资料(Cmd + I
)然后选择时间分析器。这是最昂贵的三大电话:
Running Time Self (ms) Symbol Name
6464.0ms 61.8% 5.0 specialized Array._copyToNewBuffer(Int) -> ()
1093.0ms 10.4% 6.0 String.componentsSeparatedByString(String) -> [String]
896.0ms 8.5% 0.0 specialized _VariantDictionaryStorage.updateValue(B, forKey : A) -> B?
...
(Total: 9736ms)
正如所料,为新阵列分配新内存需要花费大量时间。幸运的是,Apple已经在NSMutableArray
中为您解决了这个问题:
func fillDict2() -> [String: [Int]] {
var tmp = [String: NSMutableArray]()
for (verseIndex, verse) in verses.enumerate() {
let words = verse.componentsSeparatedByString(" ")
for word in words {
let indexArray = tmp[word] ?? NSMutableArray()
indexArray.addObject(verseIndex)
tmp[word] = indexArray
}
}
var dict = [String: [Int]]()
for (word, verses) in tmp {
dict[word] = ((verses as NSArray) as! [Int])
}
return dict
}
再次通过Instruments运行fillDict2()
,这是我得到的:
Running Time Self (ms) Symbol Name
916.0ms 21.5% 8.0 String.componentsSeparatedByString(String) -> [String]
783.0ms 18.4% 27.0 specialized _VariantDictionaryStorage.nativeUpdateValue(B, forKey : A) -> B?
754.0ms 17.7% 0.0 specialized _VariantDictionaryStorage.updateValue(B, forKey : A) -> B?
...
(Total: 3911ms)
快2.5倍!显然,你也可以寻找其他优化。这是一场永无止境的比赛。你必须确定它何时足够快。