数组概念:用一组连续的内存空间,存储一组相同类型的数据。
1. 数据之间都有哪些关系
- 无关系 :数据和数据之间是杂乱无序的。没有任何关系
- 一对一 :数据和数据之间是一对一的关系,这就是我们所说的线性表,常用的线性表:数组,链表、栈、队列。
- 一对多 :数据和数据之间是一对多的关系,例如,树结构:二叉树,红黑树等
- 多对多 :数据和数据之间是多对多的关系,例如,图结构。
总结,我们可以将数据之间的关系大致分为两类,线性结构和非线性结构。
2. 数组:连续的内存空间好不好?
2.1 随机访问
我们访问数组中的数据时,通常根据数据的下标进行数据的查看。我们申请一个长度为10的数组,存储相同类型的数据。
int[] a = new int[10];
我们从图中可知,每一个内存空间大小是一致的。当我们某一个数据时,例如 a[9]=1000 +9 *3=1039
a[i]_address = base_address(首地址) + i * data_type_size(数组每个元素的大小)
如果数组下标从1开始
a[i]_address = base_address(首地址) + (i-1)* data_type_size(数组每个元素的大小)
数组下标从1 开始,这样 cpu 每次访问数据的时候都用进行一次减法指令的操作。所以数组下标从 0 开始是不是更好一些呢。(注意:数组下标从0开始,数组越界问题一定要慎重)
通过上述讲解我们可知道数组内存空间的连续性,使得随机访问更加方便,时间复杂度为O(1),一定注意,我说的是随机访问,随机访问,随机访问。一定是随机两个词,如果查找特定的某一个数据,我们还是需要通过遍历的方法进行访问,此时查找具体某一个数据的时间复杂度依然是O(n)。
2.2 时间复杂度分析(插入和删除)
数组内存的连续性,如果我们插入或者删除某一条数据,可能造成其它数据的移动。
插入操作,最好的情况是在数组后面进行插入,此时其他数据不需要移动,如果在a[0] 位置插入数据,其它的数据都要进行移动,此时时间复杂度为O(n),删除操作同样如此。所以无论是插入还是删除操作,都可能会造成数据的移动,时间复杂度为 O(n)。
3. Java 语言中的 ArrayList
ArrayList 封装了数组内存连续性的特性,并在数组基础之上升级,如果申请空间大小是固定的,不支持动态扩容,而 ArrayList 支持动态扩容,当空间不足时,自动扩容原来的1.5倍。
创建ArrayLIst 时,需要制定数据的大小,且ArrayList 无法存储基本类型的数据,例如(int),但是存储引用类型(Integer),这就造成了装箱操作,使得CPU的性能有所消耗。
ArrayList 适合基本业务开发,数组适合于底层框架开发,两者各有优势。