手写一个ArrayList

本人文科生,转行做java开发,目前在恶补计算机基础.这篇文章就是一个学习笔记,记录自己学习ArrayList的一些理解.我是模仿java的ArrayList的实现思路写的,java里有些native的方法是自己写的,有不正确的地方希望直接指出我会改正的,不必喷我,我目前就是小白.欢迎大家批评指正.

为了方便看 我将方法大致分了四类即 增 删 改 查.另外源码中的System.arraycopy方法是native方法,我都是自己实现的.我觉得学习ArrayList 主要就是学习如何自己实现这些本地方法.

public class MyArrayList extends AbstractList
implements List,RandomAccess,Cloneable,java.io.Serializable {

//------------------------------------全局变量------------------------------------------------
private static final long serialVersionUID = 4319787461277116437L;

/**
 * Default initial capacity
 * 初始化时给的容量,该容量只有在调用add方法时 检测到是使用无参构造初始化时才会分配10个空间,
 * 含参构造不会使用到这个DEFAULT_CAPACITY
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * Shared empty array instance used for empty instances.
 * 创建对象时如果指定容量为0 则使用该对象创建一个空数组
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 用于给无参构造初始化
 */
private static final Object[] DEFAULT_EMPTY_ELEMENTDATA = {};

/**
 * object类型的数组对象
 */
transient Object[] elementData;
/**
 *
 *用于记录数组里的元素的数量
 *其实size也相当于一个指针,初始位置在0位置,每添加一个元素指针后移一位
 */
private int size;

//-------------------------------------构造方法--------------------------------------------
/**
* 带参构造函数
*
*/

public MyArrayList(int initialCapacity){
    //如果参数大于0 就创建一个该容量大小的object类型的数组
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        //如果是0 就创建一个空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        //说明参数小于0 抛出参数不合法异常
        throw new IllegalArgumentException(initialCapacity+"参数不合法");
    }
}

/**
 * 构建无参构造函数 默认生成一个空数组
 */
public MyArrayList(){
    this.elementData = DEFAULT_EMPTY_ELEMENTDATA;
}

//------------------------------查找-----------------------------------------
/**
* 此方法用于查看数组里元素的数量
* @return 元素个数
*/
public int size(){
return size;
}

/**
 *
 * @return 空为true 非空为false
 */
public boolean isEmpty(){
    return size == 0;
}

/**
 * 返回指定元素的下标 从下标0开始查找 并返回找到的第一个该元素的下标
 * 例如查找0元素的位置[5,0,-5,2,1,3,2,0]  返回值为1
 * @param o
 * @return
 */
public int indexOf(Object o){
    if (o == null){
        for(int i=0; i < size; i++){
            if (elementData[i]==null){
                return i;
            }
        }
    }else {
        for (int i=0; i < size; i++){
            if (o.equals(elementData[i])){
                return i;
            }
        }
    }
    return  -1;
}

/**
 * 查看是否包含某个元素
 * @param o
 * @return
 */
public boolean contains(Object o){
    //只要返回的下标值不是-1说明该数据存在
    return indexOf(o) >= 0;
}

/**
 * 从后往前找  返回第一个该元素的下标位置
 * 例如查找0元素的位置[5,0,-5,2,1,3,2,0]  返回值为7
 * @return
 */
public int lastIndexOf(Object o){
    if(o == null){
        for (int i = size-1; i >= 0; i--){
            if (elementData[i] == null){
                return i;
            }
        }
    }else {
        for (int i = size-1; i >= 0; i--){
            if (o.equals(elementData[i])){
                return i;
            }
        }
    }
    return -1;
}


// Positional Access Operations

@SuppressWarnings("unchecked")
E elementData(int index){
    return (E) elementData[index];
}
/**
 * 返回指定下标的元素
 * @param index
 * @return
 */

@Override
public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}

private void rangeCheck(int index) {
    if (index >= size){
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
}

private String outOfBoundsMsg(int index){
    return "index: "+index+", size: "+size;
}

//------------------------------新增元素------------------------------------

/**
 * Appends the specified element to the end of this list.
 * 往数组末尾添加元素
 * 添加时的逻辑
 * 1.调用ensureCaoacityInternal方法判断容量是否够用
 * 2.在判断容量的时候去调用ensureExplicitCapacity方法判断是否需要扩容
 * 3.在判断扩容的时候需要去确认初始化时有没有分配容量 注意即使分配的是0 也是不会去
 *   调用 return Math.max(DEFAULT_CAPACITY,minCapacity);只有在调用无参构造时才会给默认分配10个容量
 * 4.调用  ensureExplicitCapacity 判断是否需要调用grow进行扩容
 * @param e
 * @return
 */
public boolean add(E e){
    //添加之前去调这个方法 确认数组长度够不够
    //例如目前size是3 这一次添加需要去确认数组长度是不是大于等于4(长度至少是4才行),
    ensureCapacityInternal(size+1);
    //size对应元素下标 首次添加元素size是0
    elementData[size] = e;
    //每次添加完元素size加1
    size++;
    return true;
}

//minCapacity的值最小是1(数组为空时)
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData,minCapacity));
}

//calculateCapacity这个方法主要是判断在初始化时 有没有分配容量  如果没有会分配默认容量10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULT_EMPTY_ELEMENTDATA){
        //当初始化时没有给数组容量的时候 会给数组分配10个空间
        //只有使用无参构造创建对象时才会进到这里
        return Math.max(DEFAULT_CAPACITY,minCapacity);
    }
    return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    //如果当前数组长度小于 需要的最小容量 需要扩容
    if (minCapacity - elementData.length > 0){
        grow(minCapacity);
    }
}

/**
 * The maximum size of array to allocate.
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 * 因为数组还包含一些额外的头信息在里面这些信息不超过8字节
 *
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;


private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    //1.5倍扩容(右移运算)
    int newCapacity = oldCapacity + (oldCapacity >> 1);

    if (newCapacity - minCapacity < 0){
        newCapacity = minCapacity;
    }
    //当扩容后的newCapacity大于 2^31-1-8 时调用hugeCapacity
    if (newCapacity - MAX_ARRAY_SIZE > 0){
        newCapacity = hugeCapacity(minCapacity);
    }

    //新建一个数组容量为newCapacity  将旧数据放进去
    Object[] newList = new Object[newCapacity];

    for (int i = 0; i < elementData.length; i++) {
        newList[i] = elementData[i];
    }
    将elementData 指向扩容后的数组
    elementData = newList;
}

private static int hugeCapacity(int minCapacity){
    if (minCapacity < 0){
        //溢出了 所以数值小于0
        throw new OutOfMemoryError();
    }
    //如果没有溢出
    return (minCapacity > MAX_ARRAY_SIZE)
       ?Integer.MAX_VALUE
       :MAX_ARRAY_SIZE;
}

/**
 * 向指定位置插入数据
 * @param index
 * @param element
 */
public void add(int index,E element){
    //1.校验位置合法性
    rangeCheckForAdd(index);
    //2.确认是否需要扩容
    ensureCapacityInternal(size+1);
    //3.把该位置以及其后面的元素全部后移一位
    for (int i = size; i > index; i--) {
        elementData[i] = elementData[i-1];
    }
    //4.插入指定位置
    elementData[index] = element;
    //5.容量加一
    size++;
}

private void rangeCheckForAdd(int index){
    if (index < 0 || index > size){
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
}

//-----------------------------删除指定元素-----------------------------------

/**
 * 根据下标删除元素
 * @param index
 * @return
 */
public E remove(int index){
    //1.参数合法性校验
    rangeCheck(index);
    //2.记录旧数据
    E oldValue = elementData(index);

    //3.检查index是不是指向最后一个元素,
    //如果不是则需要移动元素
    int numMoved = size-1-index;//numMoved=0表示index指向最后一个元素
    if (numMoved > 0){
        //将index位置到末尾的元素依次向前移动一位
        //例如[0,1,2,3,4]如果删除元素0,则执行完数组为[1,2,3,4,4]
        //所以需要将末尾多余的4置为null,以便于GC 并且保证打印数组时里面的元素是正确的
        for (int i = index; i < size-1; i++) {
            elementData[i] = elementData[i+1];
        }
    }

    //如果numMoved=0说明删除的是末尾元素那么直接将末尾元素置为null就行
    //否则执行完if的移动元素后,再将末尾元素置为null,同时数量减一
    elementData[--size] = null;// clear to let GC do its work

    return oldValue;
}


/**
 * 根据元素删除
 * @param o
 * @return
 */
public boolean remove(Object o){
    //1.判断元素是否为null
    if (null == o){
        for (int i = 0; i < size; i++) {
            if (elementData[i] == null){
                fastRemove(i);
                return true;
            }
        }
    }else {
        for (int i = 0; i < size; i++) {
            if (elementData[i] == o){
                fastRemove(i);
                return true;
            }
        }
    }

    return false;
}

private void fastRemove(int index) {

    //1.检查是否index指向最后一个元素
    int numMoved = size-1-index;

    if (numMoved > 0){
        //将index位置到末尾的元素依次向前移动一位
        for (int i = index; i < size-1; i++) {
            elementData[i] = elementData[i+1];
        }
    }

    elementData[--size] = null;
}

//-------------------------------修改--------------------------------------

/**
 * 修改指定位置上的元素
 * @param index
 * @param element
 * @return
 */
public E set(int index,E element){

    //1.参数校验
    rangeCheck(index);
    //2.记录旧数据
    E oldValue = elementData(index);
    //3.更改数据
    elementData[index] = element;

    return oldValue;
}

/**
 * 此方法可以将未使用到的空间给裁剪掉
 */
public void trimToSize(){
    modCount++;
    //如果数组的容量大于当前数组里元素的数量 则需要裁剪
    if (size < elementData.length){
        elementData = (size == 0)
                //如果数组里没有元素 那么就把数组置为空数组
                ? EMPTY_ELEMENTDATA
                //否则需要裁剪
                : doTrim(elementData,size);
    }
    System.out.println("裁剪后的数组长度-->"+elementData.length);
}

//数组裁切方法 源码使用的是native方法,这里可以自己实现
public static <T> T[] doTrim(T[] elementData, int size) {
    Object[] newArray = new Object[size];
    for (int i=0; i<size; i++){
        newArray[i] = elementData[i];
    }
    return (T[]) newArray;
}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值