一、实现ArrayList的关键点
- 怎么存储数据?
- 怎么增删改查数据?
二、怎么存储数据?
首先想到的就是数组,用int[]?还是String[]?既然我们也不知道类型,那干脆用Objec[]来表示。所以变成了如下:
1 | java复制代码public class ArrayList { |
数组长度设置多少呢?设置多少我们也不知道呀,所以给个默认值然后也支持让用户自定义设置比较合适,如下:
1 | java复制代码public class ArrayList { |
很完美的样子,那我们容器有了,是不是就该提供api进行增删改查了?
三、API
1、增(add)
增加分为两种:直接在数组末尾增加,还一种就是我。
1.1、直接在数组末尾增加
那我们怎么知道数组中的元素的下标到哪了呢?是不是elementData.length ++
就行呢?肯定不对。elementData.length
是数组的容量,假设是10,那就代表一共可以容纳10个元素,但是很可能现在只有2个元素,也就是再add的时候应该放到下标为2(第三个元素)的位置上。
那怎么知道目前有多少元素在集合中呢?
维护个变量,每次add完都变量+1
1 | java复制代码public class ArrayList { |
很简单,就是直接添加。那我们不想再末尾追加元素,想在自定义位置追加个元素,怎么办?
1.2、在固定位置增加
怎么在固定位置追加?比如[1, 2, 3],我要在第二个位置追加4,变成[1, 4, 2, 3],那是不是需要在第二个位置以后的元素整体向后移动一位?怎么移动?
可以通过System.arraycopy()
直接复制元素到数组的位置。
1 | java复制代码public class ArrayList { |
上面移动没看懂?分析下:
1 | java复制代码System.arraycopy(elementData, index, elementData, index + 1, size - index); |
这几个参数的含义是:
1 | less复制代码src: 源数组 |
我们这里原数组和目标数组都是elementData,那不就是相当于把第index位置的数据复制到index+1上嘛?一共复制size-index个。
比如[1,2,3,4],index=1,element=5。
那么这行代码的意思是从第index开始复制(index=1,也就是第2个元素,在这里是2),将数据复制到elementData中。那复制到新数组的哪个位置呢?复制几个数据呢?
复制size-index=4-1=3个元素到新数组上,从新数组的index+1=2位置开始。所以变成了:[1,2,2,3,4]
然后最后执行了elementData[index] = element;
,那不就是elementData[1] = 5;
嘛?变成了[1,5,2,3,4]。完美。
效率不高,因为需要数组数据整体向后移动一位。
不管直接在数组末尾追加还是在固定位置增加,都有一个问题:因为数组是定长的,数组满了咋办?
只有两种解决方案:报错、自动扩容。报错显然不合适,自动扩容的机制怎么实现?
1.3、扩容
1 | java复制代码// 扩容为1.5倍 |
很简单,就是利用Arrays.copyOf()
来完成扩容。所以在add方法开始就先判断是不是需要扩容,是的话先扩容然后在add
1 | java复制代码public class ArrayList { |
2、删(remove)
也很简单吧,删除后需要把删除后的数据都向前移动一位。移动方法是arraycopy,上面add详细讲解过了,不再多说。自己代数进去看看就知道了。
1 | java复制代码public void remove(int index) { |
3、改(set)
把某个位置的元素替换成新的,然后把被替换的旧值返回回去。
1 | java复制代码public class ArrayList { |
很简单,直接赋值。O(1)时间复杂度。
4、查(get)
查的话无非就两种:根据下标查元素、根据元素查下标。
根据下标查元素
1 | java复制代码public class ArrayList { |
效率杠杠的,O(1)时间复杂度,接下来再看看根据元素查下标怎么实现。
根据元素查下标
1 | java复制代码public class ArrayList { |
这个性能就不行了,平均时间复杂度是O(n)。不过工作中遇到这种需求的也很少,我实在想不到根据元素查找元素在数组中所在的位置有何用。
四、总结
其实实现原理就是Object[],让给个默认长度10。
关键点:
- add(index, e)/remove(index)需要移动数据,效率低下。
- add(e)/add(index, e)可能需要扩容,扩容效率低下。
- get(index)直接根据index找到数组中的元素,效率极高。
五、广告
个人微信公众号:Java码农社区
本文转载自: 掘金