数组是一组元素组成的数据结构,元素类型必须相同,其次,数组内元素是连续存储的,因此数组中元素地址可以通过索引计算出来。
空间占用
在Java中,数组本质上也是一个对象,因此也存在对象头信息。那么数组的结构如下
- 8字节的markword(用来记录对象的哈希值与经历GC回收的存活次数等信息)
- 4字节的类指针(该部分存储了该数组的calss类型)
- 4字节的数组大小(间接决定了数组最大能够容纳2的32次方个元素)
- 数组元素加对齐字节(Java中所有对象大小都是8的整数倍,当存储的数组元素不是8的整数倍时,要使用对齐字节补齐)
时间复杂度
根据索引查找元素,时间复杂度是 O(1)
动态数组
静态数组在创建完毕之后,就无法更改容量大小,也不能插入和删除元素,因此,我们通常使用动态数组,而Java中也提供有默认的动态数组实现,也就是ArrayList。接下来我们自己来实现一个动态数组。
public class DynamicArray implements Iterable<Integer> {
    public DynamicArray() {
    }
    private int size = 0; //数组中元素数量
    private int capacity = 10; //数组中默认创建容量
    private int[] array = new int[capacity];
    //添加元素
    public void addList(int element) {
        checkAndGrow();
        array[size] = element;
        size++;
    }
    private void checkAndGrow() {
        //进行容量判断
        if (size == capacity) {
            //按一定比例扩容,这里扩容1.5倍
            capacity += capacity >> 1;
            //创建新数组
            int[] newArray = new int[capacity];
            System.arraycopy(array, 0, newArray, 0, capacity);
            //取代newArray
            array = newArray;
        }
    }
    //插入元素
    public void insert(int element, int index) {
        //首先进行容量判断
        if (size == capacity) {
            //进行数据扩容
        }
        //条件判断,插入的下标不能大于size
        if (index > size && index < 0) {
            return;
        }
        if (index == size) {
            addList(element);
        } else {
            //拷贝数组
            System.arraycopy(array, index, array, index + 1, size - index);
            array[index] = element;
            size++;
        }
    }
    //查询元素
    public int get(int index) {
        if (index < size && index < 0) {
            throw new RuntimeException("超出范围");
        }
        return array[index];
    }
    // 三种for循环
    public void foreach(Consumer<Integer> consumer) {
        for (int i = 0; i < size; i++) {
            consumer.accept(array[i]);
        }
    }
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            int i = 0;
            @Override
            public boolean hasNext() {
                return i < size;
            }
            @Override
            public Integer next() {
                return array[i++];
            }
        };
    }
    public IntStream stream() {
        //将一个数组中有效值转化为流
        return IntStream.of(Arrays.copyOfRange(array, 0, size));
    }
    //删除元素
    public int remove(int index) {
        int remove = array[index];
        if (index != size-1){
            System.arraycopy(array, index + 1, array, index, size - index - 1);
        }
        size--;
        return remove;
    }
}动态数组的插入与删除元素的性能分析
- 在数组头部与中间时,时间复杂度为O(n)
- 在数组尾部时,时间复杂度为O(1)
二维数组
定义二维数组的语法如下
int[][] array = {
{11, 12, 13, 14, 15},
{21, 22, 23, 24, 25},
{31, 32, 33, 34, 35},
};
内存结构如下

需要注意的是,这里虽然存在对齐字节,但在内存上仍然是连续的。
对一个二维数组 Array[m][n]
- m 是外层数组的长度,可以看作 row 行
- n 是内层数组的长度,可以看作 column 列
- 当访问 Array[i][j],0≤i≤m, 0≤j≤n时,就相当于
-  
  - 先找到第 i 个内层数组(行)
- 再找到此内层数组中第 j 个元素(列)
 
遍历二维数组时,先遍历row再遍历column效率比先遍历column再遍历row更高,原因在于CPU在读取内存中的数据时,一次性读取64字节放入缓存,而一个数组元素只有4字节,其余60字节会读取该数组元素的临近数据一起放入缓存,因此在遍历column时可以在缓存中读取数据,从而速度更快。
















![[笔记]深入解析Windows操作系统《番外》windows关键进程解释](https://img-blog.csdnimg.cn/6be383d284e64a0789a1287c699d3507.jpeg)


