一、概述
在计算机科学中,数组是由一组元素(值/变量)组成的数据结构,每个元素至少有一个索引/键来标识
数组内元素是连续存储的,所以数组中元素的地址可以通过其索引计算出来,例如
int[] array = {1,2,3,4,5};
知道数组数据的起始地址 B a s e A d d r e s s BaseAddress BaseAddress,可由公式: B a s e A d d r e s s + i ∗ s i z e BaseAddress + i * size BaseAddress+i∗size 计算得出索引 i i i 所在元素地址
- i i i 即索引,在 Java、C 等语言都是从 0 开始
- s i z e :每个元素占用字节, i n t 占 4 , d o u b l e 占 8 size:每个元素占用字节,int\ 占\ 4,double\ 占\ 8 size:每个元素占用字节,int 占 4,double 占 8
二、性能
2.1 空间占用
Java 中数组结构为
- 8 字节 markword
- 4 字节 class 指针(压缩情况)
- 4 字节数组大小(决定数组最大容量是 2 32 2^{32} 232)
- 数组元素 + 对齐字节(Java 所有对象大小都是 8 字节的整数倍,不足的用对齐字节补齐)
例如:
int[] array = {1,2,3,4,5};
array 数组大小为:8 + 4 + 4 + (5 * 4 + 4(对齐字节)) = 40 字节
2.2 时间复杂度
查询
- 根据索引查询,时间复杂度: O ( 1 ) O(1) O(1)
- 根据值查询,时间复杂度: O ( n ) O(n) O(n)
根据值查询时,循环遍历,线性查找
插入/删除
- 头部位置,时间复杂度: O ( n ) O(n) O(n)
- 中间位置,时间复杂度: O ( n ) O(n) O(n)
- 尾部位置,时间复杂度: O ( 1 ) O(1) O(1)
头部/中间位置插入/删除,涉及元素的前后移动,线性
尾部插入时可能会发生扩容,平均来看时间复杂度: O ( 1 ) O(1) O(1)
三、二维数组
3.1 内存占用
- 二维数组占用 32 字节,其中 array[0],array[1],array[2] 分别保存指向三个一维数组的引用指针
- 三个一维数组各占 40 字节
- 内存布局上是连续的
四、数组的缓存与局部性原理
CPU | 缓存 | 内存 |
---|---|---|
皮秒 ( 1 0 − 12 ) (10^{-12}) (10−12) | 接近于CPU | 纳秒 ( 1 0 − 9 ) (10^{-9}) (10−9) |
缓存:提升 CPU 读写速度的效率。缓存行(cache line)64字节
空间局部性:缓存会将内存中相邻的 64 字节数据读入缓存中
局部性原理
- CPU 读取内存(速度慢)数据后,会将其放入高速缓存(速度快)中,如果后来的计算再用到此数据,在缓存中能读到的话,就不用再去内存读取
- 缓存的最小存储单位是缓存行(cache line),一般是 64 bytes,一次最少读取 64 bytes 填满一个缓存行。因此读入某数据时也会读取其临近数据,这就是所谓的空间局部性