带着问题进入下面的阅读:为什么数组下标从 0 开始而不是 1 呢?
如何实现随机访问?
定义
数组是一种线性表数据结构,用连续的内存空间来存储一组具有相同的数据类型的数据。
- 线性表
- 连续的内存空间和相同的数据类型
正是有了这两个特性:「随机访问」。但是,当我们需要删除或者插入的时候也需要做大量的搬移工作。
说到数据访问, 数组是如何实现根据下标随机访问数组元素 ?如下图:
给数组 a[10] 分配了一块连续内存空间 1000-1039 ,其中内存块的首地址为:base_add=1000
需要随机访问数组中的某个元素时,它会首先通过下面的寻址公式,计算出该元素存储的内存地址:
a[i]_add = base_add + i * data_type_size
所以,这里加入从 1 开始计数,那么公式应为:
a[i]_add = base_add + (i-1) * data_type_size
如此,每次计算都要减一,所以这是数组从 0 开始的原因之一。
低效的插入和删除
因为数组是连续的内存空间,当我们想要在一个数组的中间插入或者删除一个数据,就需要移动部分数据,这很好理解的。
- 插入
当不需要保证数据有序的时候:
- 把要插入位置的元素移动数组末尾
- 把元素插入位置
当需要保证数据有序的时候:
- 将数组插入位置到数组末尾的所有元素都依次往后移动一位
- 把元素插入位置
- 删除
数组 a[10] 中存储了 8 个元素:a,b,c,d,e,f,g,h。现在,我们要依次删除 a,b,c 三个元素。
为了避免 defgh 这个几个元素多次搬移:
- 先将元素标记为删除,并不真正移动
- 当数组满了,再统一删除
这就是: JVM 标记清除垃圾回收算法的核心思想
警惕数组越界
Java 中不存在数组越界问题,因为会直接抛出错误;如下代码:
public class TestObject {
public static void main(String[] args) {
int[] a=new int[3];
a[0]=1;
a[1]=2;
a[2]=3;
for(int i=0;i<=3;i++){
System.out.println(a[i]+",");
}
}
}
//outPut:
1,
2,
3,
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at testAll.ThinkingInJava.TestObject.main(TestObject.java:18)
容器 VS 数组
如果你是做 Java 业务开发,那么 99.9% 的时间都是直接使用容器(ArrayList)而不是数组;
数组
int[] i={}; //大小为0
int[] i1=new int[5]; //5
int[] i2=new int[]{}; //0
//不能同时指定大小,又初始化数据
//比如:int[] i2=new int[5]{};
Java 中数组本身在定义的时候需要预先指定大小,因为需要分配连续的内存空间。如果我们申请了大小为10的数组,当第11个数据需要存储到数组中时,我们就需要重新分配一块更大的空间,将原来的数据复制过去,然后再将新的数据插入
ArrayList
我们不用关心底层扩容逻辑,每次存储空间不够的时候,他都会自动将空间扩容为 1.5 倍大小。
因为扩容涉及内存申请和数据搬移,所以如果能够事先确定存储的数据大小,最好在创建 ArrayList 的时候就指定数据大小
总结对比:
- 在 Java 日常开发中都是直接使用容器,封装了实现细节,并且可以动态扩容
- ArrayList 无法存储基本数据类型
来源:CSDN
作者:NCharming
链接:https://blog.csdn.net/weixin_41793772/article/details/103464004