「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战」
数组定义
一种线性表数据结构,用一组连续的内存空间,来存储一组具有相同类型的数据。
如何实现随机访问?
- 线性表,用连续的存储空间存储相同类型数据。
- 寻址表达式:
arr[i]_address = base_address + i*data_type_size - 复杂度分析:
- 随机访问:O(1)
- 二分查找:O(logn)
低效的插入&删除
为了保持内存数据的连续性,导致插入、删除操作比较低效。
- 复杂度分析:
- 最好-末尾:O(1)
- 最坏-开头:O(n)
- 平均复杂度:O(n)
- 特殊插入:如果数组不要求数据有序,可通过插入是先取出插入位置元素放到队尾,插入元素,替换插入位置元素,则复杂度为O(1)
- 标记删除: 先将要删除元素标记删除并不实际删除,未删除元素也不清理,直到内存不足或触发删除条件是,再清理数据,重排元素(参考JVM的标记清除垃圾回收算法)。
数组容器-ArrayList
优点
- 封装数组操作细节,比如插入、删除。
- 支持动态扩容,比如,每次存储空间不够时,会将空间自动扩容1.5倍大小。
- 动态扩容涉及内存申请和数据迁移,如若确定最好事先指定数据大小。
缺点
- Java ArrayList无法存储基础数据类型,比如int/long,需要封为Integer/Long类,Autoboxing/Unboxing需要一定性能损耗。
- 如果大小事先已知,并对数据操作简单,则用不到容器提供功能,也可直接数组。
数组与容器选择,底层开发,性能追求极致,数组优于容器;否则推荐容器。
思考:
- 前面我基于数组的原理引出 JVM 的标记清除垃圾回收算法的核心理念。我不知道你是否使用 Java 语言,理解 JVM,如果你熟悉,可以在评论区回顾下你理解的标记清除垃圾回收算法。
- 前面我们讲到一维数组的内存寻址公式,那你可以思考一下,类比一下,二维数组的内存寻址公式是怎样的呢?
问题一:
标记清除算法,核心标记、清除两阶段,先标记处需回收对象,最后再统一触发回收清除,减少频繁的GC阻塞。
问题二:
二维数组的寻址表达式: object arr[][] = new object[n][m] arr[i][j]_address = base_address+(i*n+j)*data_type_size