ArrayList是java的一种集合类,它的底层是一个可变数组,可以存放任何Object的协变类型的引用,即java中的所有对象。通过模拟实现ArrayList集合类,可以帮助我们了解其底层实现原理,以及简单泛型的使用。
在MyArrayList类中有一个泛型数组,用于存放元素,以及最大容量capacity和目前存放元素的数量size,目的是为了方便统计,以及后面的扩容操作。给该类提供了一个无参的构造方法(默认初始容量为10),和带参构造方法(指定初始容量)。需要注意的是data为泛型数组,然而泛型数组不能直接new,我们暂且只能先创建一个Object类型的数组然后将其强制类型转换为泛型数组。
public class MyArrayList{ private int capacity;//最大容量 private T[] data; private int size;//目前存储元素的数量 public MyArrayList(){ this.capacity=10; this.data=(T[])new Object[capacity]; } public MyArrayList(int capacity) { this.capacity = capacity; this.data=(T[])new Object[capacity]; }
然后我们为其提供添加元素的方法。使用者可以默认直接添加一个元素在数组末端,或者是将一个元素添加到指定位置,此时就需要将后面的元素依次往后移一位。需要注意的是添加元素前需要先检查顺序表是否已满,如果已装满则需要先扩容。当使用者指定下标时,需要先校验下标是否合法,不合法时需要处理。每当使用者添加一个元素后,size需要加一。
public void add(T element){
//在当前顺序表的末端添加一个元素
if(isFull()){
dilatation();//扩容方法
}
this.data[this.size]=element;
this.size++;
}
public void add(int index,T element){
//在指定下标位置添加元素,需校验指定下标的合法性
if(isFull()){
dilatation();
}
if(index>this.size){
System.out.println("数组下标越界!");
return;
}
for(int i=this.size;i>index;i--){
this.data[i]=data[i-1];
}
this.data[index]=element;
this.size++;
}
下来我们实现扩容方法。扩容方法只有在顺序表已满的情况下才会被add方法调用,这个方法无须使用者调用,我们用private封装起来。 数组扩容的原理:我们先创建一个大容量的数组,然后将原来的数组内容拷贝到新的数组中,然后将顺序表对象中的数组引用指向新的数组。这部分有一个问题需要特别注意,顺序表扩容后的容量默认为原来的1.5倍,但如果原顺序表的容量为1,由于capacity为int类型的,计算扩容后的容量结果依旧为1,此时如果add直接添加新元素将会出现数组下标越界的异常。我们可以为其甚至一个默认最小扩容单位,让扩容·的大小不小于这个值。这个值也不能小于add每次添加的最少元素数量。
private boolean isFull(){
if(this.size==this.capacity){
return true;
}
return false;
}
private void dilatation(){
int min=5;
if(min>this.capacity>>1){
this.capacity=this.capacity+min;
}else{
this.capacity=this.capacity+this.capacity>>1;
}
this.data= Arrays.copyOf(this.data,this.capacity);
}
接下来我们为顺序表提供删除元素的方法。这里可对比添加元素的方法,将需要删除的位置用其后方的元素覆盖即可,然后依次覆盖,将最后一个元素置为null,size减一。清空顺序表还可以直接将data置为null,至于原来的那块空间未被引用就会被jvm回收。
public void remove(int index){
//删除指定下标位置的元素
if(index>this.size-1){
System.out.println("数组下标越界!");
return;
}
for(int i=index;i
查找和改写元素的方法较为简单,按照下标查找的话需要返回泛型类的元素。
public int search(T element){
//在顺序表中搜索element元素,若能找到则返回找到的第一个相同元素的下标
//若不能找到则返回-1
for (int index=0;indexthis.size-1){
System.out.println("数组下标越界!");
return null;
}
return this.data[index];
}
public void set(int index,T element){
//将指定下标的元素修改为指定元素
if(index>this.size-1){
System.out.println("数组下标越界!");
return;
}
this.data[index]=element;
}
public int length(){
return this.size;
}
public void print(){
for(int i=0;i
为方便测试,我们为其编写了length方法和print打印方法。测试中,当我们往顺序表中放基本数据类型的话,需要装箱,所以我们为其提供的泛型类型应该为它们的包装类。
public class Main{
public static void main(String[] args) {
MyArrayList array = new MyArrayList<>(1);
array.add(1);
array.add(2);
array.add(3);
array.add(4);
array.add(4, 5);
array.print();
array.remove(0);
array.print();
}
}



