数组(一)基础介绍

基本介绍

在每种编程语音中,都存在数组这种数据结构。它不仅是编程语音中的一种数据结构,也是最基础的一种数据结构。例如java中的ArrayList,底层的数据结构便是数组。
那么什么是数组呢?看下比较官方的定义:数组(Array)是一种线性表数据结构,它用一组连续的内存空间,来存储一组具有相同类型的数据。从定义中有几个重要的特点:1、线性表 2、连续内存和相同数据类型

线性表

线性表就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。其实除了数组,链表、队列、栈等也是线性表结构。
在这里插入图片描述

与它相对立的概念是非线性表,比如二叉树、堆、图等。之所以叫非线性。在非线性表中,数据之间并不是简单的前后关系。
在这里插入图片描述

连续内存和相同数据类型

正式因为这个特性,所以数组支持随机访问,通过定位到下表访问该位置存储的值,效率为O(1)。但有利就有弊,这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的移动操作。

插入操作

假设数组的长度为n,如果我们需要将一个数据插入到数组中的第k个位置。为了把第k个位置腾出来,给新来的数据,我们需要将第k~n这部分的元素都顺序地往后挪一位。
我们来计算下插入操作的时间复杂度。当插入最后一位时,无需移动元素,时间复杂度为O(1);当插入第一位时,需要将数组所有的元素往后移动一位,则时间复杂度为O(n)。平均情况下时间复杂度为1+2+…n)/n=O(n)。

在这里插入图片描述
如果数组中存储的数据并没有任何规律,数组只是被当作一个存储数据的集合。如果要将某个数据插入到第k个位置,可直接将第k位的数据搬移到数组元素的最后,把新的元素直接放入第k个位置,这样时间复杂度则为O(1)。
在这里插入图片描述

删除操作

跟插入数据类似,如果我们要删除第k个位置的数据,为了内存的连续性,也需要搬移数据,不然内存就不连续了。
时间复杂度和插入类似,当删除最后一位时,时间复杂度为O(1),当删除第一位时,时间复杂度为O(n),平均情况下时间复杂度为O(n)。
在这里插入图片描述
很多情况下,我们不需要保证数组必须连续的,可采用逻辑删除的方法。当删除K位置元素时,不是真实删除该元素,而是标记K位置的元素为删除状态,查询时跳过删除状态的元素,若数组大小已满不可再插入时,触发真实删除操作,将删除状态的元素删除,移动后面的元素。这样可将多次删除后移动元素操作合并到一次,大大减少元素的移动操作。
在这里插入图片描述
熟悉JVM垃圾回收的同学,是不是觉得有点相似的感觉。垃圾回收中标记清除算法,和逻辑删除运用了相同的思想。

使用场景

java同学每次与各种容器打交道,最常见的一定是ArrayLIst。ArrayLIst的底层就是数组,那么ArrayLIst和数组的区别是什么?什么时候改用数组?什么时候该用ArrayLIst?
ArrayList最大的优势是将很多数组操作的细节封装起来。比如数组插入、删除数据时需要搬移其他数据等。另外还有一个优势,就是支持动态扩容。

动态扩容

当使用数组时,初始化需要定义数组的大小,因为需要在内存中给数组分配一块连续可用的空间。当数组使用大小打到数组容量时,则需要先将数组扩容,一般是先定义原数组1.5倍的容量,后将原数组拷贝到新数组,然后做插入操作。

在ArrayList的插入时,已实时了动态扩容功能,当无法插入时会生产新数组后插入至新数组中。需要注意一点,因为扩容操作涉及内存申请和数据搬移,是比较耗时的。如果事先能确定需要存储的数据大小,最好在创建ArrayList的时候事先指定数据大小。

//ArrayList中数组扩容操作
private void grow(int minCapacity) {
        //原数组容量
        int oldCapacity = elementData.length;
        //新数组容量,为原数组容量1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //拷贝原数组元素至新数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

数组使用场景

正常业务开发,使用容器足以,因为无需关注底层操作,可以已一点性能为代码,不影响整体系统性能。但如果是底层开发,比如框架、中间价等,对性能有极高的要求,这时候用数组就是首选。
总结以下场景更适合使用数组:
1、数据大小已知且对数据的操作非常简单,用不到ArrayList提供的大部分方法,也可以直接使用数组
2、Java ArrayList无法存储基本类型,比如int、long,需要封装为Integer、Long类,而装箱、拆箱则有一定的性能消耗,所以如果特别关注性能,或者希望使用基本类型,就可以选用数组。
3、当使用多维数组时,用数组更直观。用数组表示为:Object[][] array,用容器表示为 ArrayList list。

总结

数组可以说是最基础、最简单的数据结构。数组用一块连续的内存空间,来存储相同类型的一组数据,最大的特点就是支持随机访问,但插入、删除操作也因此变得比较低效,平均情况时间复杂度为O(n)。
在平时的业务开发中,我们可以直接使用编程语言提供的容器类,但是,如果是特别底层的开发,直接使用数组可能会更合适。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值