JAVA实现自定义实现的ArrayList

那,为了更加深刻理解ArrayList的底层原理实现,就写一个自定义的ArrayList集合,实现出底层的基本功能,关于一些底层的原理,可以参考文章:
https://blog.csdn.net/weixin_46726346/article/details/107562529

好,言归正传,现在开始写:

1、arraycopy的用法

最开始想介绍一个方法arraycopy这个方法的使用,因为这个方法会被用到;
我们用代码加图解来介绍:

		int arr[]=new int[5];
		for(int i=0;i<arr.length;i++) {
			arr[i]=i;
		}

写了一个大小为5的数组,值分别为01234对吧;
在这里插入图片描述
看图,我现在想把3,4这两个元素去替换前面的1,2这两个元素,看我的操作:

System.arraycopy(arr, 3, arr, 1, 2);

就这一行代码,那是什么意思呢:

public static void arraycopy(Object src, int srcPos, Object dest,int destPos,int length)

从指定源数组中复制一个数组,复制从指定的位置开始,到目标数组的指定位置结束。从 src 引用的源数组到 dest 引用的目标数组,数组组件的一个子序列被复制下来。被复制的组件的编号等于 length 参数。源数组中位置在 srcPos 到 srcPos+length-1 之间的组件被分别复制到目标数组中的 destPos 到 destPos+length-1 位置。

然后数组就变成了:

在这里插入图片描述

好,这个方法理解了,我们就开始写这个自定义的ArrayList!

首先肯定要创建好类,然后再给类加个泛型:

public class MyArrayList<E> {}

然后就开始写了

2、成员变量

我们在上一篇中说过,ArrayList底层是一个数组,
那么成员变量 最主要的就是要有一个数组来存储元素

private Object[] elementData;

然后要有记录元素个数的成员变量

private int size;

然后在初始化的时候,要有一个空数组(上一篇说过就不详细说了)

private Object[] emptyArray[]= {};

再有一个第一次扩容的初始容量10,应该定义为常量

private final int capcity=10;

3、构造方法

说完成员变量,第一个方法肯定是构造方法;
我们这里只写一个无参数的构造方法
我们知道在调用无参数构造方法的时候会初始化一个容量为0的数组:

	public MyArrayList() {
		elementData=emptyArray;
	}

这就用到了之前定义的成员变量!

4、扩容方法

关于扩容方法,我们知道,第一次调用add函数的时候,会把容量设置为10;
然后每次添加元素,数组不够了之后,会吧容量扩容至原来的1.5倍:
这里的1.5倍,我们是有移位运算符来写的(底层就是)

	private void grow() {
		if(elementData==emptyArray) {
			elementData=new Object[capcity];
		}
		if(size==elementData.length) {
			int oldCpacity=elementData.length;
			int newCapcity=oldCpacity+(oldCpacity>>1);
			Object obj[]=new Object[newCapcity];
			System.arraycopy(obj, 0, obj, 0, oldCpacity);
			elementData=obj;
		}
		
	}

然后这个方法不被外界调用,所以我们设置成私有的,由于只有在调用add方法的时候,才会调用grow方法,所以我们写一下add方法;

5、add方法

我们知道add方法返回值是布尔类型,并且把元素添加到尾部,size加一;
每次调用add方法都要调用一次grow方法:

	public boolean add(E e) {
		grow();
		elementData[size++]=e;
		return true;
	}

6、toString方法

我们上篇说过,toString方法底层是通过迭代器进行遍历,我们自己写就用随机访问来进行遍历数组,输出字符串,在这里最好不要用+来写,用StringBuilder来写来减少开销:

	public String toString() {
		if(size==0) {
			return "[]";
		}
		
		StringBuilder str=new StringBuilder();
		str.append("[");
		for(int i=0;i<size;i++) {
			if(i==size-1) {
				str.append(elementData[i]).append("]");
			}
			else {
				str.append(elementData[i]).append(",").append(" ");
			}
		}
		
		return str.toString();
	}

7、set方法

set方法,返回值是被修改的元素,我们要判断索引的位置是否有错,如果正常,就可以直接修改元素了!

	public E set(int index,E element) {
		if(index<0||index>size) {
			throw new IndexOutOfBoundsException("索引越界");
		}
		E value =(E) elementData[index];
		elementData[index]=element;
		return value;
	}

8、remove方法

如果我们要删除一个元素,首先要判断位置是否有错误,然后把索引位置的元素删除,后面所有的元素向前一位,哎,这里就用到了arraycopy这个方法:

	public E remove(int index) {
		if(index<0||index>size) {
			throw new IndexOutOfBoundsException("索引越界");
		}
		E value =(E) elementData[index];
		int numMoved=size-index-1;
		if(numMoved>0) {
			System.arraycopy(elementData, index+1, elementData, index, numMoved);
		}
		elementData[--size]=null;
		return value;
	}

就相当于把要删除的元素,后面所有的元素复制到前一个位置上,然后再把最后的一个元素置空;
返回值呢,就是被删除的元素;

9、get方法

获取元素就更简单了,判断索引,正常的话直接返回就好了,返回元素个数也是一样的!

	public E get(int index) {
		if(index<0||index>size) {
			throw new IndexOutOfBoundsException("索引越界");
		}
		return (E) elementData[index];
	}
	public int getSize() {
		return size;
	}

到此基本的方法就全写完了,然后我们在主函数里面测试一下:

10、test函数

	public static void main(String[] args) {
		MyArrayList<String> list=new MyArrayList();
		list.add("123");
		list.add("456");
		list.add("789");
		list.set(1, "abc");
		list.remove(2);
		System.out.println(list.get(0));
		System.out.println(list.getSize());
		System.out.println(list);
	}

然后我们看一下输出:
123
2
[123, abc]

也是木有问题的,好了,到此就结束了,关于ArrayList就这样了!

猜你喜欢

转载自blog.csdn.net/weixin_46726346/article/details/107580973