ArrayList的底层原理是用数组实现的,所以我们就可以自定义实现一个ArrayList,主要实现一些简单的方法,其实有的方法本质上也是数组的拷贝,目的是深入体会底层原理,加深对ArrayList容器的理解。

首先:我们需要一个自定义数组,元素大小,默认容量和有参无参构造方法,以便创建默认或者指定容量大小的容器。

/**
     * 自定义数组
     */
    private Object[] elementData;

    /**
     * 数组实际元素大小
     */
    private int size;

    /**
     * 默认容器的容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    public SXArrayList(){
        elementData = new Object[DEFAULT_CAPACITY];
    }

    public SXArrayList(int capacity){
        elementData = new Object[capacity];
    }

其次:添加add方法,给容器添加元素。添加元素之前要判断是否需要扩容,扩容的本质也是数组的复制,定义一个更大的数组,再把原来的数组复制过去。什么时候需要扩容呢?就是当容器满了的时候,我们就定义一个新的数组,容量就是旧数组容量+旧数组容量的一半(注意这里是数组容量大小,不是元素大小),这里我们用位运算,效率高,最后把新数组的引用赋给旧数组。

public void add(Object obj){
        //如果容量满了 就需要扩容了
        if (size == elementData.length){
            Object[] newArray = new Object[elementData.length + (elementData.length >> 1)];
            System.arraycopy(elementData, 0, newArray, 0, size);
            elementData = newArray;
        }
        elementData[size++] = obj;
    }

再次:添加remove移除方法,可以根据索引或者元素内容移除对应的元素。移除等操作前需要判断索引是否合法,移除的本质也是数组的复制,覆盖掉要移除的元素,假如移除的是最后一个元素就可以直接赋值为null就行。根据内容移除就是遍历容器找到内容相等的元素下标再执行remove操作。

/**
     * 移除容器中指定索引的元素内容
     * @param index 索引
     */
    public void remove(int index){
        indexCheck(index);
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        elementData[--size] = null;
    }

    /**
     * 移除容器中指定的元素内容
     * @param obj 元素内容
     */
    public void remove(Object obj){
        if (null != obj){
            for (int i = 0; i < size; i++) {
                if (obj.equals("" + elementData[i])){
                    remove(i);
                    break;
                }
            }
        }
    }
    private void indexCheck(int index){
        if (index < 0 || index >= size){
            throw new IndexOutOfBoundsException("索引不存在:" + index);
        }
    }

再次:添加set方法,给容器中指定索引设置元素内容。这个也要判断索引是否合法,然后就直接给指定元素重新赋值。这里要重写toString方法,返回一个格式内容字符串,可以用于打印容器内容。

/**
     * 给容器中指定索引设置元素内容
     * @param index 索引
     * @param obj 元素内容
     */
    public void set(int index, Object obj){
        indexCheck(index);
        elementData[index] = obj;
    }

    /**
     * 重写toString方法
     * @return 返回元素内容
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < size; i++) {
            sb.append(elementData[i]).append(",");
        }
        sb.setCharAt(sb.length()-1,']');
        return sb.toString();
    }

最后:添加get,size等简单的方法,最后main方法简单测试一下,结果正常,后期还可以完善添加泛型,加深理解和记忆。

public static void main(String[] args) {
        SXArrayList list = new SXArrayList();
        for (int i = 0 ;i < 26;i++){
            list.add((char)(i+97));
        }
        System.out.println(list);
        list.remove(25);
        System.out.println(list);
        list.remove("d");
        System.out.println(list);
        System.out.println(list.get(2));
        list.set(0,"aa");
        System.out.println(list);
    }

[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z]
[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y]
[a,b,c,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y]
c
[aa,b,c,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y]

完整代码如下:

/**
 * 手工实现ArrayList
 */
public class SXArrayList {

    /**
     * 自定义数组
     */
    private Object[] elementData;

    /**
     * 数组实际元素大小
     */
    private int size;

    /**
     * 默认容器的容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    public SXArrayList(){
        elementData = new Object[DEFAULT_CAPACITY];
    }

    public SXArrayList(int capacity){
        elementData = new Object[capacity];
    }

    /**
     * 获取容器元素大小
     * @return 返回大小
     */
    public int size(){
        return size;
    }

    /**
     * 判断容器里是否有元素内容
     * @return 返回是否
     */
    public boolean isEmpty(){
        return size == 0;
    }

    /**
     * 给容器添加元素
     * @param obj 添加的元素内容
     */
    public void add(Object obj){
        //如果容量满了 就需要扩容了
        if (size == elementData.length){
            Object[] newArray = new Object[elementData.length + (elementData.length >> 1)];
            System.arraycopy(elementData, 0, newArray, 0, size);
            elementData = newArray;
        }
        elementData[size++] = obj;
    }

    /**
     * 获取容器中指定索引的元素内容
     * @param index 索引
     * @return 返回元素内容
     */
    public Object get(int index){
        indexCheck(index);
        return elementData[index];
    }

    /**
     * 移除容器中指定索引的元素内容
     * @param index 索引
     */
    public void remove(int index){
        indexCheck(index);
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index + 1, elementData, index, numMoved);
        elementData[--size] = null;
    }

    /**
     * 移除容器中指定的元素内容
     * @param obj 元素内容
     */
    public void remove(Object obj){
        if (null != obj){
            for (int i = 0; i < size; i++) {
                if (obj.equals("" + elementData[i])){
                    remove(i);
                    break;
                }
            }
        }
    }

    /**
     * 给容器中指定索引设置元素内容
     * @param index 索引
     * @param obj 元素内容
     */
    public void set(int index, Object obj){
        indexCheck(index);
        elementData[index] = obj;
    }

    /**
     * 重写toString方法
     * @return 返回元素内容
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < size; i++) {
            sb.append(elementData[i]).append(",");
        }
        sb.setCharAt(sb.length()-1,']');
        return sb.toString();
    }

    /**
     * 检查索引是否越界
     * @param index 索引
     */
    private void indexCheck(int index){
        if (index < 0 || index >= size){
            throw new IndexOutOfBoundsException("索引不存在:" + index);
        }
    }
}