在Swift中搜索大型数组的性能

4

我有一个包含全球所有机场的JSON文件,并尝试在函数中进行搜索,但搜索速度非常慢,因此需要提高其性能。该JSON文件中有9500个条目(我本来想使用Web API,但找不到免费的,所以使用了这个JSON文件)。我的数组形式如下:

var data = [Dictionary<String, AnyObject>]

这是其中一个字典的样子:
[DisplayText: YYZ, airportObject: {
    0 = 193;
    Altitude = 569;
    City = Toronto;
    Country = Canada;
    DST = A;
    IATA = YYZ;
    ICAO = CYYZ;
    Latitude = "43.677223";
    Longitude = "-79.630556";
    Name = "Lester B Pearson Intl";
    TZ = "America/Toronto";
    UTC = "-5";
}, DisplaySubText: Lester B Pearson Intl]

目标是在用户输入时,TextField自动完成机场名称。我编写了一个函数来过滤用户输入的条目。它可以工作,但非常缓慢,每输入一个字母需要1秒钟,而且CPU使用率会达到50%。
下面是该函数:
func applyFilterWithSearchQuery(filter : String) -> [Dictionary<String, AnyObject>]
{
    //let predicate = NSPredicate(format: "DisplayText BEGINSWITH[cd] \(filter)")
    var lower = (filter as NSString).lowercaseString
    var filteredData = data.filter({
            if let match : AnyObject  = $0["DisplayText"]{
                //println("LCS = \(filter.lowercaseString)")
                return (match as NSString).lowercaseString.hasPrefix((filter as NSString).lowercaseString)
            }
            else{
                return false
            }
        })
    return filteredData
}

如何提高这个函数的性能?

1
我会将文件分割成更小的部分,例如将所有以A开头的机场复制到a.json,B到b.json等等。当用户开始输入搜索时,只需搜索以用户键入的第一个字母命名的文件。 - Greg
1
只有你能决定。使用工具!调整和测试!但一个明显的建议是:不要在过滤器中重复你可以事先完成的事情。例如,提前使用lowercaseString。 (为什么你要使用lower而且甚至没有使用它???)此外,你可能会发现NSString的方法来确定match是否以filter开头比hasPrefix更快。 - matt
1
将JSON文件放入数据库文件(CoreData最方便),并在获取请求中使用NSPredicates。停止重复转换筛选条件为小写,使用预先转换的版本。缓存结果以便下一次使用(当您已经有一个A列表时,无需为AB过滤完整列表,只需重新过滤子列表即可)。 - David Berry
1
将字典数组转换为机场结构体数组,在我的测试中,这样可以将执行时间减少到约三分之一。确保你不要在playground中进行性能评估。 - David Berry
1个回答

3

我考虑了所有评论,并得出以下结论。

  1. 我更改了逻辑,只有输入2个字符后才运行applyFilterWithSearchQuery。
  2. 每次结果后缓存结果,只搜索子列表。
  3. 我更改了函数,按照以下方式使用NSPredicate:

    func applyFilterWithSearchQuery(filter : String) -> [Dictionary<String, AnyObject>]
    {
        let predicate = NSPredicate(format: "DisplayText BEGINSWITH[cd] %@", filter)
        let filteredData = (self.data as NSArray).filteredArrayUsingPredicate(predicate!)
        return filteredData as [Dictionary<String, AnyObject>]
    }

总体 CPU 时间降至 1%。感谢所有提出建议的人。


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