自写ArrayList(一)
1.引言
我们为什么需要ArrayList这样的数据结构呢?这就得从我们的普通数组说起。假如说我们定义一个普通数组:
//整型
int[] array = new int[10];
我们可以发现,在申请这个一维整型数组的时候,必须声明这个数组的长度,声明之后这个数组的长度就不能改变了,也就是定长。然后,我们在使用数组下标对数组元素进行操作时,会容易造成越界,取到错误位置的数据之类的问题。我们还可以发现,在申请数组的时候,同时必须声明这个数组的类型,就比如说上例声明了int类型,在往数组里存储元素的时候必须存声明类型的元素。不仅限于上述三种情况,我们不难发现普通的数组会产生某些不够“灵活”的问题,有一些些的限制,我们不能随心所欲地使用它。
所以java有ArrayList这个数据结构,来方便我们的数组使用。本文将自写ArrayList类——MyArrayList,来加深我们对这个数据结构的理解。我将会分两篇文章来讲述,这是第一篇~(( * ^ ▽ ^ *))。
2.实现前的分析
我们想要自己写一个“随心所欲”使用的数组,需要哪些功能呢?
-
首先本质是数组:
– 相应属性:长度,下标,元素个数
– 初始化:需要我们写相应的构造方法
(1)空参数。默认配置初始化
(2)输入参数是初始长度。根据输入的初始长度来初始化,需要考虑输入的长度是否合法。 -
基本方法(增删查改)
- 增
(1)按下标存储,自动递增下标。
(2)存满了怎么办?我们就要对数组进行扩容。创建容量更大的新数组,将原数组中元素都复制过来,然后替换引用即可。
(3)回收空间。因为java有垃圾回收处理,替换引用后原数组所占的空间会自动释放。- 删
(1)根据下标来删除:删除一个,并且元素自动前移,补空位。
(2)根据输入的元素来删除:输入元素后会进行查找,找到所有满足条件的元素,全部删除,同时所有元素自动前移,补空位。- 查
(1)根据下标查询:返回对应下标的元素
(2)根据元素查询:返回所有满足条件元素的下标数组。- 改
先根据下标来查询,然后再替换。
-
加入自己想实现的功能:
比如说我加入了排序功能:如果数组存储的是整型,就可以对其进行排序,所以在写这个方法的时候必须对数组进行检查,看是否是整型。如果不是,就要报错;如果是,就返回排序好的数组。
3.具体实现
3.1创建MyList接口
首先创建一个MyList接口(不熟悉接口的小伙伴可以看我之前的文章( * ^ ▽ ^ * ),link.),把想要实现的方法先抽象写入接口中,方便我们以后继承。比如说我写完ArrayList之后,我还想自己写其他的数据结构,就可以继承MyList接口,毕竟“增删查改”是最基本的操作嘛,这样能使我们的代码结构更有层次,更好优化。下面我们创建接口:
public interface MyList {
//返回队列中元素个数
int size();
//添加
void add(Object value);
/**
* 查找
* @param index 根据下标查找
* @return 返回元素对象
*/
Object get(int index);
/**
* 查找
* @param obj 需要查找的元素
* @return 返回下标数组
*/
int[] get(Object obj);
/**
* 移除
* @param index 需要移除的下标位置
*/
void remove(int index);
/**
* 移除
* @param obj 需要移除的元素
*
*/
void remove(Object obj);
//替换
void replace(int index,Object obj);
//输出List中所有元素
void out();
/**
如果存储的是整数 就可以排序
@return 返回排序后的Integer类型数组
*/
Integer[] sort();
}
3.2创建MyArrayList类
于是乎,通过继承,我们就可以创建自己写的MyArrayList类了,对于接口中的方法一定要重写。
主要讲一下实现根据输入元素来删除,和最后排序这两个方法。
- (1)根据输入元素来删除,找到所有满足条件的元素,全部删除,同时所有元素自动前移,补空位:
public void remove(Object obj) {
//get()返回的num数组,num[0]是找到的元素个数
int[] num = this.get(obj);
Object[] arrayValue1 = new Object[length];
int k = 1;//num的下标
int j = 0;//arrayValue1的下标
for(int i=0; i<size; i++){
if(i==num[k]){
k++;
continue;
}
arrayValue1[j]=arrayValue[i];
j++;
}
size-=num[0];
arrayValue=arrayValue1;
//arrayValue1=null;
}
首先这里用到了我自己写的查询方法int[] get(Object obj),返回的是一个整型数组num。其中,num[0]存的是找到输入元素的个数,就是输入的obj能找到几个,num[0]的值就为几。然后,从num[1]开始往后存这些被找到元素的下标。
具体的思路就是,先得到num数组,然后新建一个arrayValue1用来完成原来arrayValue的复制工作 (为方便叙述,arrayValue1下文用a1表示,arrayValue下文用a表示)。i表示当前数组a的下标,j表示当前数组a1的下标,k表示当前数组num的下标。那么i从0开始一直到size(元素总个数)都有元素,j从0开始一直到size-num[0]才有元素,因为删除后数组会变短嘛。因为num[1]开始存储的就是要删除元素的下标,i到相应的位置跳过就行了,j不需要加,i和k加一。所以就可以实现,数组a中被删除元素以外的所有元素都可以按顺序复制到数组a1中。最后我们只需要把a1的引用给a,把a原来申请的空间释放即可。
- (2)排序功能:如果数组存储的是整型,就可以对其进行排序,所以在写这个方法的时候必须对数组进行检查,看是否是整型。如果不是,就要报错;如果是,就返回排序好的数组:
public Integer[] sort() {
for(int i=0; i<size; i++){
if(!(arrayValue[i] instanceof Integer)){
System.err.println("数列队伍里不全是Integer,不能排序!");
return null;
}
}
Integer