前言
由于我们之前的栈以及后面的队列都是用数组实现的。所以我们了解下数组对于了解数据结构很有帮助。
C语言的世界里由于数组内存是连续的所以,所以删除和添加元素的时候会memmove内存的元素,如下所示。交流群昵称:ios-Swift/Object C开发上架
添加
删除
总结:如果元素很多,移动效率会很低。
iOS中数组是怎么优化的呢?
iOS数组中为了优化上述问题采用了,circular buffer的数据结构来解决。
在头部删除
在头部添加
总结:在两端插入和删除都很快
在中间删除
总结:在中间插入或者删除尝试移动最小的内存量,因此它将最多移动一半的元素。
访问数组中的元素第一种情况
offset是2,size是5。我们获取数组index 0 的元素,所以要获取元素的offset等于2+0。由于size 5 ,大于获取元素的offset 2 。所以这时候数组index 0 的元素就是list[2],也就是如图offset所指的元素。
访问数组中的元素第二种情况
获取数组index 0 的元素,这时候获取的元素的offset等于4+0,则就是如图所示offset指向的元素。如果获取数组index 2 的元素,这时候获取的元素的offset等于4+2=6,但是6>size(5),所以需要减去size也就是(4+2)- 5=1,也就是如图offset1指向的元素。
总结所以获取数组元素的效率也是很高的。
数组的容量问题
-
我们尽量在刚开始能预估容量大小,以防止频繁的扩容造成的效率问题。
-
一旦有大量元素存在,然后你又删除的所剩无几,数组空间不会自动减小,需要你自己清除空间,这个技巧我们在栈中使用过。
结合数组API来分析
addObject
如果当前容量很充足,我们添加元素到数组是O(1)的操作。如果此时正好容量用完,需要扩充容量,这时候需要创建新的容器并copy过去,这时候是O(n)。
insertObject
如果在头尾两处插入,在容量充足情况下效率最高O(1)的操作。但是如果在中间插入则需要移动最多一半的元素,这时候是O(n)的操作。
removeLastObject
在尾部或者头部删除是O(1)操作。
removeObjectAtIndex
但是如果在中间删除则需要移动最多一半的元素,这时候是O(n)的操作。
initWithCapacity
上面已经介绍如果能提前预估容量可以有效避免扩容造成的性能问题。
这个扩容的过程是创建一个新的容器分配更大的空间,然后把旧的容器的元素们copy进来。这个扩容发生是恒定的时间,随着容量的变大,发生的次数会越来越少。