数组包含太慢的Swift

时间:2017-01-06 08:44:24

标签: arrays swift performance contains

我一直在将我在Java(Android)中使用的算法移植到Swift(iOS),并且在Swift版本上遇到了一些速度问题。

基本思想是有深度的对象(评论树),我可以通过匹配隐藏对象列表来隐藏和显示数据集的回复。以下是可视化

Top
- Reply 1
- - Reply 2
- - Reply 3
- Reply 4

并从数据集中隐藏

Top
- Reply 1
- Reply 4

我从Java转换的相关方法如下

 //Gets the "real" position of the index provided in the "position" variable. The comments array contains all the used data, and the hidden array is an array of strings that represent items in the dataset that should be skipped over.

    func getRealPosition(position: Int)-> Int{

        let hElements = getHiddenCountUpTo(location: position)
        var diff = 0
        var i = 0
        while i < hElements {
            diff += 1
            if(comments.count > position + diff && hidden.contains(comments[(position + diff)].getId())){
                i -= 1
            }
            i += 1
        }
        return position + diff
    }

    func getHiddenCountUpTo(location: Int) -> Int{
        var count = 0
        var i = 0
        repeat {
            if (comments.count > i && hidden.contains(comments[i].getId())) {
                count += 1
            }
            i += 1
        } while(i <= location && i < comments.count)
        return count
    }

这与UITableViewController一起用于将注释显示为树。

在Java中,使用array.contains足够快,不会导致任何延迟,但Swift版本在调用heightForRowAt和填充单元格时多次调用getRealPosition函数,导致越来越多的注释ID被添加到“隐藏”数组。

有什么方法可以提高数组“包含”查找的速度(可能使用不同类型的集合)?我对应用程序进行了分析,“包含”是占用时间最多的方法。

谢谢

2 个答案:

答案 0 :(得分:6)

Java和Swift都必须遍历数组中包含的所有元素。随着数组变大,这会越来越慢。

没有先验的理由让Java更好,因为它们都使用完全相同的算法。但是,在每种语言中字符串的实现方式都不同,因此可以在Swift中使字符串比较更加昂贵。

在任何情况下,如果字符串比较会降低你的速度,那么你必须避免它。

轻松修复:使用套装

如果想要简单的性能提升,可以用一组字符串替换字符串数组。 Swift中的一个集合用哈希表实现,这意味着你有预期的恒定时间查询。实际上,这意味着对于大型集合,您将看到更好的性能。

    var hiddenset Set<String> = {}
    for item in hidden {
      strset.insert(item)
    }

为获得最佳性能:使用BitSet

但你应该能够比一套能做得更好。我们来看看你的代码

    hidden.contains(comments[i].getId()))

如果您总是以这种方式访问​​hidden,那么这意味着您拥有的是从整数(i)到布尔值(true或false)的映射。

然后你应该做以下......

    import Bitset;

    let hidden = Bitset ();
    // replace hidden.append(comments[i].getId())) by this:
    hidden.add(i)
    // replace hidden.contains(comments[i].getId())) by this:
    hidden.contains(i)

然后你的代码真的会飞!

要在Swift中使用快速BitSet实现,请在Package.swift中包含以下内容(它是免费软件):

    import PackageDescription

    let package = Package(
        name: "fun",
        dependencies: [
       .Package(url: "https://github.com/lemire/SwiftBitset.git",  majorVersion: 0)
        ]
    )

答案 1 :(得分:1)

我认为你需要realPosition从tableview中的一行点击链接到源数组?

1)创建第二个数组,其中只包含tableViewDataSource

的数据

将所有可见元素复制到此新数组。创建一个特殊的ViewModel作为类或更好的结构,只有在tableview中显示的nessesary数据。在这个新的ViewModel中保存realdataposition也作为值。现在你有一个到源数组的反向链接

2)然后仅从新数据源

填充此TableView

3)进一步了解swift中的functional programming - 例如,你可以更好地浏览数组:

var array1 = ["a", "b", "c", "d", "e"]
let array2 = ["a", "c", "d"]
array1 = array1.filter { !array2.contains($0) }

或在你的情况下:

let newArray = comments.filter{ !hidden.contains($0.getId()) }

或枚举以创建viewmodel

struct CommentViewModel {
  var id: Int
  var text: String
  var realPosition: Int
}

let visibleComments: [CommentViewModel] = comments
   .enumerated()
   .map { (index, element) in
      return CommentViewModel(id: element.getId(), text: element.getText(), realPosition: index)
   }
   .filter{ !hidden.contains($0.id) }