![440bd464689e2d9322e36db70b77e5ee.png](https://i-blog.csdnimg.cn/blog_migrate/d0835a38e7b92fb370a33c3fc6d7fc64.png)
初学者的每日学习笔记 2020.12.2
数组
数组的定义
数组(array)是一种线性表数据结构,它用一组连续的内存空间来存储一组具有相同类型的数据。
数组的特点
主要由其定义的关键词就可以发现:
1.线性表:线性表就是数据排成一条线,每个线性表上的数据最多只有前和后两个方向。除了数组,链表、队列、栈等也是线性表结构。
与此同时,二叉树,堆(特殊的二叉树),图等就是非线性的。
2.连续的内存空间:
![fef68e3f6fefa285b47a5850625dfec2.png](https://i-blog.csdnimg.cn/blog_migrate/3bbd4e5422e31cf3449ba0063bad4077.jpeg)
从图中可以看到,计算机给数组a分配了一块连续的内存1000-1039.其中内存块的首地址为base_address=1000。
a[i]_address = base_address + i*data_type_size
这里的data_type_size表示数组中每个元素的大小,因为例子中是int所以大小为4。
(补充:32位机器上,一个整数占4字节,一个地址占4个字节,因为二进制数(11111111111111111111111111111111)个字节是最大的存储量,所以内存只能到4G)。
与之形成对比的是,python中的数据结构列表(list)的存储方式
![3ab7473bef4ddafac614d012463dfe1d.png](https://i-blog.csdnimg.cn/blog_migrate/818f977844138068d859f9c7fa543481.jpeg)
其实这就代表了两种存储方式,一种是与前面介绍的数组存储方式相同,按连续的内存空间来存储,另外一种就是列表中存的是元素所对应的内存地址,然后通过地址的指向得到值。
这样的存储方式的差异就带来了以下的数组的优缺点:
优点:
1.按照索引查询元素速度快;
2.按照索引遍历数组方便。
缺点:
1.数组的大小固定后就无法扩容了;
2.数组只能存储一种类型的数据;
3.添加,删除的操作慢,因为要移动其他的元素。
为什么数组的插入和删除是低效的?
插入操作:
假如数组的长度为n,如果我们需要将一个数据插入到数组中的第k个位置,为了把第k个位置腾出来,需要把第k~n这部分的元素都顺序的往后挪一位,操作复杂。
如果在数组的末尾插入元素,那就不需要移动数据了,插入比较简单,但是如果在数组的头部插入元素就复杂了,需要把所有的数据都往后移动一位。
如果一个数组中的数据是有序的,那我们在某一个位置插入一个新的元素的时候就必须得按照上面的方法移动k之后的数据,但是数组中存储的数据如果没有任何规律,这时候,如果想要把某个元素插入到k位置,为了避免大规模的数据搬移,有个简单的办法就是:直接把k位置的数据搬到数组的最后面,把新元素放到第k个位置。
删除操作:
跟插入数据类似,如果我们要删除第k个位置的数据,为了内存的连续性,也需要搬移数据,不然中间就会出现空洞。
在某些情况下,如果我们不要求数据必须是连续的,那么删除的时候可以不真删除,只是把这个元素标记为已删除,当组空间不够用的时候,在触发一次真正的删除,这样就大大减少了删除操作导致的搬移操作。
其实JVM的核心垃圾算法就是这个思想
3.只能存储相同类型的数据:
原因就是第二个定义造成的:每种数据类型所占空间大小不同,比如char占1个字节,int占4个字节,如果数组里面存了多种数据类型,那就不方便根据索引去计算地址偏移量了,实现不了Random Access。如果你存放了多种该数据类型,那么你必须用其他的数据结构去记录哪个位置是什么类型,比如你数组的第一位放了一个byte,如果你不记录,当你取用的时候,你怎么知道他是byte类型呢,而不是char呢。所以说只存一种数据简单纯粹。另外扩容问题,假设你申明了int[10],那么JVM会分配40个字节的空间,然后一段时间后,这40个字节空间的后面被其他对象占了,如果你原地扩容,那不把其他的对象改写了么。
补充知识:
ArrayList
java中的ArrayList相当与一个动态数组,支持扩容。它可以把很多数组操作的细节封装起来,比如数组的插入和删除。
数组在定义的时候,因为需要给它分配连续的内存空间,需要预先指定其大小,当存放的数据大于其大小的时候,我们需要重新分配一块更大的空间,把原来的复制过去在插入新的元素。
ArrayList中,当空间不够用的时候,它会自动扩容为原来的1.5倍的大小。
因为扩容设计到内存的申请和数据的搬移,这是比较耗时的,所以,如果事先能确定数据的大小,做好在创建ArrayList的时候指定其大小
虽然ArrayList比数组好用,不过有些时候使用数组会更好一些。
1)因为ArrayList无法存储基本类型,int long等需要封装成Integer,Long类,而自动装箱和拆箱的操作也会有一定的性能消耗,所以如果关注性能或者想用基本类型就选用数组
2)如果数据的大小已经知道,并且对数据的操作简单,可以直接使用数组
3)当使用多为数组的时候,用数组表示起来更加直观比如int[][]arr;而用容器的话则需要这样定义ArrayList arr。
在开发中,一般情况下直接使用ArrayList就可以,如果是偏底层的开发比如网络框架开发等需要更高的性能,使用数组更合适。
参考:
数据结构之数组_mingyunxiaohai的专栏-CSDN博客_数据结构数组blog.csdn.net![57e2765782313499f65c4e05078ac033.png](https://i-blog.csdnimg.cn/blog_migrate/67d5d19f87d94bcf9677e8f6e378af9a.png)