数组
数组(Array)是一种线性表数据结构,用连续的内存空间,存储相同类型的数据
数组特性
线性表
线性表(Linear List)数据是线性排列的,每个线性表中的数据最多只有一个头和一个尾(或者一个前一个后),线性表数据结构包括:数组、队列、栈、链表等
非线性表中,数据之间并不是前后关系,一个数据可能跟多个数据有关系,非线性表包括 二叉树、堆、图等。
连续的内存空间
如图所示,系统给数组分配了一个连续的内存空间,即数组各个内元素的存地址也是连续的
当我们需要访问到数组的某个位置的元素时,可以根据首地址与数组中元素类型所占的内存大小以及数组角标,计算出目标元素的地址:
数组某个元素的内存地址 = 数组首地址+数组角标*数组元素类型的大小
有了地址了,就可以获取到元素值具体是多少。
为什么数组的下标一般是从0开始,可以从这个公式看出一点端倪
如果下边从1开始,那么
数组某个元素的内存地址 = 数组首地址+(数组角标-1)*数组元素类型的大小
每次随机访问某一个元素的时候都要执行减1操作,这对CPU来说,就是多执行了一次减法指令;
还有一个原因,可以从Java语言的起源来说,C语言的数组的下标是从0开始的,后来或许为了降低开发者的学习成本,Java数组效仿了C语言数组,下标也是从0开始。
支持随机访问
根据上述描述,我们可以知道数组可以支持随机访问,且根据下标随机访问的时间复杂度是O(1)
数组的插入和删除数据效率低
因为数组是连续的内存空间,如果插入数据到数组的开头位置的元素,或者删除开头位置的元素,都要把数组中其它数据向后或者向前挪,从而保证内存的连续性。
如果插入数据的位置是数组末尾或者删除的数据是数组末尾,则不需要移动数据。
根据以上分析,最好时间复杂度是O(1)最坏时间复杂度是O(n),平均时间复杂度是1* 1/n+2* 1/n…n* 1/n = O(n)
改进方法:
插入:
如果数组的值是无序的,设想要插入数据的角标位置为a,取出角标a下的数组的值,放入数组的最后的位置,然后将需要插入的值放入原来a的位置。改进后时间复杂度为O(1);如果数组是有序的,那只能按照之前移动数据的方式,并不能改进和优化
删除:
如果数组的值是无序的,如果想要删除数组头部的三个元素,正常情况下每次删除都要移动剩下的元素。改进的方式是:先记录这个三个要删除的元素,当数组空间满的时候,再进行删除之前记录的元素。(JVM的标记清除算法核心思想)这样优化后,不用每次执行删除都移动元素了,但是最后数组满了进行删除的时候,还是需要遍历到数组的最后,时间复杂度是O(n)。
Java中容器类List和数组如何选择
1.数组只能存储基本数据类型,List中存储的是基本类型的包装类,使用中会涉及到装箱和拆箱操作,是有性能消耗的。如果特别追求性能,使用数组
2.数组不支持动态扩容,除非自己实现动态扩容,List支持动态扩容,如果事先数据个数已知,可以用数组
总结
数组(Array)是一种线性表数据结构,用连续的内存空间,存储相同类型的数据,特点是可以随机访问数组元素,插入数据和删除数据效率低,平均时间复杂度是O(n)。实际开发中,一般使用Java提供的容器类,当最求极致的性能比如说写网络相关的框架等,在能用数组的地方尽量使用数组。