数组
什么是数组
数组是指一组连续的内存存储位置。数组中的每一个位置称为元素,它们具有相同类型并且使用下标去指示。
每一个索引下标对应一个相同数据类型的值。如下是我偷的一张图:
数组下标是连续的,数据存储地址也是连续的,存储数据的类型是统一的。
数组特点
数组的数据形式
数组可以分为一维数组,二维数组,多维数组。
一维数组顾名思义是一个维度的,每个数组单位存储数据。
二维数组顾名思义是两个维度的,每个数组单位存储的是一个一维数组。
n维数组每个数组单位存储的是(n-1)维数组。
java创建数组:
- int []a[] = new int[6][6];
- int a[][] = new int[6][];(使用时必须初始化每个一维数组),比如: a[0] = new int[5];
- int[][]a = new int[][]{{1,2},{1,3,4}};
- int[][]a = new int[][]{}; 不报错但无法使用,它不拥有存放数据的内存空间
上述写法都是不会报错的。
数组操作
本人画图水平有限,将就看😂😂😂😂
数组使用场景
存储固定长度的数据,因为数组长度不可变
实现栈和队列:通过控制元素的添加和移除,数组可以方便实现栈和队列这种先进先出的数据结构。
需要频繁查找元素的场景,连续存储,访问快
Java中的数组实现😎
Java里面有一个数组的实现类ArrayList
, 它是Java里的一个典型的数组结构的类。
但是你可能会发现,它居然不用指定长度,而且我想加多少数据就加多少数据,不是说数组长度是固定的吗😅😅😅
不要急,是因为它有扩容机制,当满了就去扩容数组,将原数组的数据复制到新数组。
ArrayList特性
- 初始容量为10,扩容之后为原来的1.5倍
- 底层使用Object数组实现,支持快速随机访问
为了加深对ArrayList的理解,我们手写一个简单的ArrayList。
手写ArrayList🤩
首先,我们得搭建基本的类框架,最终类的结构如下图所示:
同样,使用泛型,为了类型安全。
从上到下依次是:
- 当前数组大小
- 数组目前的容量
- 存放数组的Object数组, 不能是E[], 因为该数组是需要初始化的,而泛型是不能初始化的。
构造函数
构造函数里我们设定数组初始大小,初始化Object数组。
public FakeArrayList() {
capacity = 10;
elements = new Object[capacity];
}
扩容函数
当前数组大小等于最大容量(没法添加新元素时),这时候就需要对数组进行扩容了。
实现的原理就是创建一个新数组,然后将旧数组的的值复制进去。
private void grow(){
// 表示需要扩容
if (size==capacity){
System.out.println("触发扩容");
// 扩容为1.5倍
int newCapacity = (capacity >> 1) + capacity;
// 改变当前的容量
capacity = newCapacity;
// 复制到新数组
elements = Arrays.copyOf(elements, newCapacity);
}
}
小心这里有个坑,+号的运算级别高于位运算符,所以位运算符那里需要添加括号。不然结果就是0了。
添加数据
我这里是写了两种添加方法,一种是默认的在末尾添加,另一种是在指定索引位置添加
// 末尾添加
public void add(E e) {
// 判断是否扩容
grow();
// 添加数据
elements[size++] = e;
}
// 指定位置添加
public void add(int index, E element){
// 检查索引有无越界, 越界就调用add()函数加到末尾,我这里就没写了,所以我们不能添加超过索引的
grow();
// 将index之后及其index的数据往后移动一位
System.arraycopy(elements, index, elements, index+1, size-index);
// 添加数据
elements[index] = element;
size++;
}
删除跟这个添加也差不多,我就不赘述了。
获得数据
public E get(int index) {
// 我没有写越界判断,所以不要查索引之外的
return (E) elements[index];
}
测试一下
public static void main(String[] args) {
FakeArrayList list = new FakeArrayList();
for (int i = 0; i < 11; i++) {
list.add("hhh" + i);
}
list.add("11");
System.out.println(list.get(11));
list.add(3, "替换索引3的数据");
System.out.println(list.get(3));
System.out.println("查看数据:");
for (int i = 0; i < list.size; i++) {
System.out.println(list.get(i));
}
}
输出
触发扩容
11
替换索引3的数据
查看数据:
hhh0
hhh1
hhh2
替换索引3的数据
hhh3
hhh4
hhh5
hhh6
hhh7
hhh8
hhh9
hhh10
11
先验证扩容,添加十一个数,可以看到触发了扩容
然后验证末尾添加,发现添加成功
再然后,替换索引为3的数据,再查看发现成功
最后,打印整个数组,观看最后的数据结构与我们预期的是否相同,验证成功。
成功完成基本的ArrayList的编写,原来的ArrayList也基本是这个逻辑。