数组是一种线性的数据结构.它同一组连续的内存空间,来存储一组具备相同类型的数据。数组
简单说明几点:数据结构
(1).线性表:就是数据排成像一条线同样的结构。每一个线性表的数据最多只有前和后两个方向。除了数组,链表,队列,栈等也是线性表结构。spa
对立的是非线性表,好比二叉树,堆,图等之因此被称为非线性,是由于,在非线性表中,数据之间并不简单的先后关系。code
(2).数组是连续的内存空间和相同的数据类型。正是由于这样数据的随机访问的速度很快,有利就有弊,好比在数组中删除和插入一个数据,为了保证连续性,就须要大量的数据搬用 的工做。blog
咱们拿一个长度为 10 的 int 类型的数组 int[] a = new int[10] 来举例。在我画的这个图 中,计算机给数组 a[10],分配了一块连续内存空间 1000~1039,其中,内存块的首地址 为 base_address = 1000。索引
咱们知道,计算机会给每一个内存单元分配一个地址,计算机经过地址来访问内存中的数据。 当计算机须要随机访问数组中的某个元素时,它会首先经过啊a[i]_address=base-address+i * data_type_size 的寻址公式,计算出该元素 存储的内存地址.队列
其中 data_type_size 表示数组中每一个元素的大小。咱们举的这个例子里,数组中存储的是 int 类型数据,因此 data_type_size 就为 4 个字节。这个公式很是简单,我就很少作解释 了。内存
,数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)ci
假设数组长度是n ,如今须要将一个数据插入到数组的第K个位置,为了吧k位置挪出来,给新来的数据,咱们须要吧k—n 这部分的元素顺序日后移动一位,那么时间的复杂度是多少呢?element
下面咱们来分析一下:
若是在数组的末尾插入元素,那就不须要移动元素了,那么时间的复杂度为O(1),若是在数组的开头插入元素,那么全部的数据须要一次向后移动一位,因此最坏的时间复杂度是O(N),由于咱们在每一个位置插入元素的几率是同样的,因此平均时间复杂度(1+2+。。。。+n)/n=O(n)
若是数组中的数据是有序的,咱们在某一个位置插入一个新元素时,就必须搬移k以后的数据。可是数组中的数据是无序的,没有规律,数据只是存储数据的集合。在这种状况下,若是要将元素插入到K位置,为了不大规模的数据移动,之间将K位置的数据搬移到数组元素的最后,把新的元素之间放到第K个位置。删除的操做,为了内存的连续性,也须要移动数据,和插入相似,若是删除数组末尾的数据,则最好状况时间复杂度为 O(1);若是删除开头的 数据,则最坏状况时间复杂度为 O(n);平均状况时间复杂度也为 O(n)。
实际上,在某些特殊场景下,咱们并不必定非得追求数组中数据的连续性。若是咱们将屡次 删除操做集中在一块儿执行,删除的效率是否是会提升不少呢?
咱们继续来看例子。数组 a[10] 中存储了 8 个元素:a,b,c,d,e,f,g,h。如今,我 们要依次删除 a,b,c 三个元素,为了不 d,e,f,g,h 这几个数据会被搬移三次,咱们能够先记录下已经删除的数据。 每次的删除操做并非真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存 储数据时,咱们再触发执行一次真正的删除操做,这样就大大减小了删除操做致使的数据搬移。
(1).在Java中提供的容器类,ArrayList最大的优势就是能够将不少数组操做的细节封装起来,便于调用,另外一个优势是支持动态扩容。
(2).数组自己在定义的时候预先指定了大小,由于须要分配连续的内存空间,若是咱们分配了大小为10的数组,当第11个元素须要存储到数组当中时,咱们须要从新分配一块更大的空间,将原来的数据复制过去,而后将新的数据插入。
(3).使用ArrayList,咱们彻底不须要关心底层的扩容逻辑,ArrayList 已经帮咱们实现好了,存储空间不够的时候,会将空间自动扩容1.5倍的大小。
(4).ArrayList没法存储基本的数据类型,(8种基本数据类型),而是引用类型,因此基本数据类型,能够选用数组.
public class ArrayTest { private Object[] elementData; //定义一个空的数组 private int size; //数组的大小 //初始话数组的大小 public ArrayTest() { elementData = new Object[10]; } /** * 增长元素 */ public boolean add(Object obj) { ensureCapacityInternal(size + 1); elementData[size++] = obj; return true; } //主要的判断数组是否须要扩容 private void ensureCapacityInternal(int minCapaCity) { if (minCapaCity - elementData.length > 0) { grow(minCapaCity); } } //数组扩容的操做 private void grow(int minCapaCity) { int oldcapaCity = elementData.length; int newCapaCity = oldcapaCity + oldcapaCity >> 1; if (newCapaCity - minCapaCity < 0) { newCapaCity = minCapaCity; } if (newCapaCity - Integer.MAX_VALUE > 0) { newCapaCity = Integer.MAX_VALUE; } elementData = Arrays.copyOf(elementData, newCapaCity); } /** * 删除数组中的元素,经过索引 */ public Object remove(int index) { rangCheck(index); Object oldValue = elementData[index]; int moveNum = size - index - 1; //数组移动的次数 if (moveNum > 0) { System.arraycopy(elementData, index + 1, elementData, index, moveNum); } elementData[--size] = null; return oldValue; } //查询指定索引下数组的元素 public Object get(int index) { rangCheck(index); return elementData[index]; } private void rangCheck(int index) { if (index < 0 && index > size) { throw new IndexOutOfBoundsException("index:" + index + "size:" + size); } } public static void main(String[] args) { ArrayTest arrayTest = new ArrayTest(); for (int i = 0; i < 10; i++) { arrayTest.add("第" + i + "个元素"); } arrayTest.remove(3); for (int i = 0; i < 9; i++) { System.out.println(arrayTest.get(i)); } } }
数组用一块连续的内存空 间,来存储相同类型的一组数据,最大的特色就是支持随机访问,但插入、删除操做也所以 变得比较低效,平均状况时间复杂度为 O(n)。在平时的业务开发中,咱们能够直接使用编 程语言提供的容器类,可是,若是是特别底层的开发,直接使用数组可能会更合适。