数组的定义
数组是一种线性表数据结构,它用一组连续的内存空间存储一组具有相同类型的数据。
线性表数据结构
线性表结构是一种数据结构,用于存储一组有序的数据元素。线性表中的数据元素按照线性的顺序排列,每个元素只有一个直接前驱和一个直接后继。
随机访问特性
随机访问是指可以通过元素的位置或下标(索引)直接访问元素的一种访问方式。对于线性表结构来说,随机访问是指可以根据元素在线性表中的位置,以O(1)的时间复杂度访问到元素。这意味着,无论线性表中有多少元素,访问任何一个元素的时间都是相同的,而不受元素数量的影响。
a[i]_address = base_address + i * data_type_size
arr[i] 首地址 = 数组内存块首地址 + 数据类型大小 * i ,其中i为偏移量。
baseaddress:内存块的首地址。datatype_size:数组中每个元素的大小,比如每个元素大小是4个字节。
数组使用二分法查找元素,时间复杂度是O(logn)
根据下标随机访问的时间复杂度是O(1)
数组低效的插入和删除
数组在插入和删除元素时,可能需要进行大量的元素移动,因此在这些操作上可能不是非常高效。具体来说:
插入元素:如果要在数组的中间插入一个元素,那么需要将插入位置之后的所有元素都向后移动一位,为新元素腾出空间。这样的操作需要进行大量的数据移动,其时间复杂度为O(n)。
删除元素:如果要删除数组中的一个元素,那么需要将该元素之后的所有元素都向前移动一位,填补被删除元素留下的空缺。这也需要进行大量的数据移动,其时间复杂度同样为O(n)。
为什么数组要从 0 开始编号,而不是1
在计算机科学中,数组通常从 0 开始编号,而不是从 1 开始编号。这是由于历史原因造成的,并在早期的计算机科学中广泛使用。下面是几个原因:
方便计算:从 0 开始编号可以更方便地计算元素的地址。例如,对于数组中的元素 a[i],它的地址可以计算为数组的起始地址加上 i * sizeof(a[i])。
与指针算术的兼容性:在 C 语言中,指针算术的规则是将指针加上一个整数值,该整数值表示要访问的元素在数组中的偏移量。如果从 1 开始编号,则会使指针算术的规则变得复杂,因为需要将指针减去 1,然后加上要访问的元素的编号。
历史原因:早期的计算机科学家和编程语言设计者普遍采用从 0 开始编号的习惯,这在后来的编程语言中得到了继承和保留。
需要注意的是,并不是所有的编程语言都采用从 0 开始编号的方式。例如,Fortran 编程语言从 1 开始编号,而 MATLAB 编程语言允许用户根据需要选择从 0 或 1 开始编号。
原生数组与容器类
原生数组的优点:
直接存储数据,访问速度快,因为数据在物理内存中是连续存储的。
数组大小固定,不会因为数据量变化而引起额外的内存分配和释放,因此在大量数据读写的场景中更加高效。
原生数组的缺点:
插入和删除元素的效率低,因为需要移动其他元素来填补空缺。
只能存储相同类型的元素,不支持泛型,因此使用起来不够灵活。
数组大小固定,无法动态调整,需要手动重新分配内存空间。
容器类的优点:
可以动态增加和删除元素,不需要手动分配内存空间,使用起来更加灵活。
支持泛型,可以存储不同类型的元素。
提供了丰富的API和算法,如排序、查找、删除等,使用起来更加方便。
容器类的缺点:
在大量数据读写的场景中,效率比原生数组低。
由于底层实现是链表或者动态数组,因此占用的内存空间比原生数组更大。
由于支持泛型,因此在存储元素时需要进行类型检查,会带来额外的性能开销。
java中的数组
Java数组是一个存储固定大小元素的容器,所有元素必须是相同类型的。Java数组实际上是对象,由JVM(Java虚拟机)在运行时创建和管理。
Java数组有以下几个重要的特性:
声明数组时需要指定数组的长度,一旦数组创建,其长度就不能再改变。
数组中的元素可以通过索引进行访问,索引从0开始。
Java数组可以存储基本数据类型,也可以存储对象类型。
Java数组是连续的内存块,可以通过内存地址进行访问。
Java数组可以被初始化为指定的值,或者使用默认值初始化。
在Java中,数组使用方括号 [] 来声明,例如:
int[] arr = new int[10];
这将声明一个名为 arr 的整数数组,其长度为10。可以使用 arr[0]、arr[1] 等来访问数组的元素。
Java还提供了一些方便的方法来处理数组,例如:Arrays.sort() 可以对数组进行排序,Arrays.toString() 可以将数组转换为字符串,Arrays.copyOf() 可以复制数组等等。
Java 数组本身是无法实现真正的删除操作的,因为数组的长度是固定的,一旦创建,就不能再改变长度,只能改变数组中的元素值。因此,Java 中实现删除操作的一种常用方法是通过标记待删除元素的方式,然后在后续操作中跳过这些被标记的元素。
具体来说,当需要删除某个元素时,可以将该元素位置的值置为 null,表示该位置的元素已经被删除。然后,在后续操作中遍历数组时,可以通过判断元素是否为 null 来跳过这些已经被删除的元素。当数组中已经被标记删除的元素数量达到一定程度时,可以通过一些特殊的方式来重新组织数组,以减小已删除元素对数组性能的影响。