Java|ArrayList源码分析

本文结构:
1.介绍特点
2.基本方法
3.重点源码分析

1.介绍特点

ArrayList:
是List的一个具体实现子类,是List接口的一个数组实现 (里面必定维护了一个数组)。
默认初始容量10, 扩容机制1.5倍。(数组必然有初始容量和扩容机制)
有序。
允许null元素。
允许重复元素。
线程不安全。

2.基本方法

关键字 简介
add 增加
contains 判断是否存在
get 获取指定位置的对象
indexOf 获取对象所处的位置
remove 删除
set 替换
size 获取大小
toArray 转换为数组
addAll 把另一个容器所有对象都加进来
clear 清空

3.重点源码分析

源码解析:

import java.util.Arrays;
import java.util.ConcurrentModificationException;

public class MyArrayList<E>{

    private static final int DEFAULT_CAPACITY = 10;// 默认容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 最大容量

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA ={} ;//空数组


    private Object[] elements;//底层维护的数组
    private int size;//容器内对象的个数
    private int modCount;//记录ArrayList这个对象被修改的次数
    //构造方法
    public MyArrayList() {
        this.elements = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }



    //获取列表存的数据量
    public int getSize() {
        return size;
    }

    /**
     *增加内容
     * @param e
     * @return
     */
    public boolean add(E e) {
        //创建一个默认为10的数组,若小则扩容



        ensureCapacityInternal(size+1);
        //确保容量够,则添加数据,并讲数据大小加1.
        elements[size]=e;
        size++;
        return true;
    }

    //该方法来确保数组的容量够用,若为创建则创建数组并赋予默认值。
    private void ensureCapacityInternal(int minCapacity) {
        //若数组为空(还未添加数据)
        if (elements==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
            //则选出默认值和最小容量的最大值
            minCapacity=Math.max(DEFAULT_CAPACITY,minCapacity);
            //只add方法其实没必要比较,主要是addAll()这个方法需要比较
        }

        modCount++;//修改次数加一

        //判断一下是否需要扩容,若数据+1大于当前数组,则需要扩容
        if(minCapacity-elements.length>0){
            grow(minCapacity);//调用扩容方法
        }
    }

    /**
     * 具体的扩容方法
     * @param minCapacity
     */
    private void grow(int minCapacity){
        int oldCapacity=elements.length;//获取原始数组的长度
        int newCapacity=oldCapacity+(oldCapacity>>1);//扩容 1+0.5 倍
        //若扩容后的长度比所需要的最低长度还要小,则直接把扩容的长度更改为最低所需长度
        if (newCapacity-minCapacity<0){
            newCapacity=minCapacity;
        }
        //若扩容完的新长度比规定的最大容量还大,则要进一步判断,并进一步修改数组大小
        if (newCapacity-MAX_ARRAY_SIZE>0){
            //若所需的容量竟然小于0,说明超过Int最大值,越界了,抛出异常
           if (minCapacity<0){
               throw new OutOfMemoryError();
           }
           //若所需的容量不超过Int最大值,则再判断
            if (minCapacity>MAX_ARRAY_SIZE){//若所需的大于数组要求的而小于Int最大值
                newCapacity=Integer.MAX_VALUE;//直接赋值Int最大值
            }else{//否则只有小于数组最大值这一种情况了,赋予数组最大值即可
                newCapacity=MAX_ARRAY_SIZE;
            }
        }
        elements = new Object[newCapacity];

    }

    /**
     * 根据下标添加元素
     * @param index 下标
     * @param element 要替换的元素
     */
    public void add(int index,E element){
        //先检查一下下标是否越界
        rangeCheck(index);
        //确保数组长度够用
        ensureCapacityInternal(size+1);
        //这里调用System里面一个静态的本地方法(效率更高),用于数组的复制。这里把下标后面的都向后移了。
       /* public static native void arraycopy(Object src,  int srcPos, Object dest, int destPos, int length);
            Object src:the source array. 源数组
            int srcPos:starting position in the source array. 在源数组中,开始复制的位置
            Object dest:the destination array. 目标数组
            int destPos:starting position in the destination data. 在目标数组中,开始赋值的位置
            int length:the number of array elements to be copied. 被复制的数组元素的数量*/
        System.arraycopy(element, index, element, index + 1, size - index);//等于说把下标后面的都向后移了。
                        //原数组 开始的位置 目标数组   目标数组中开始复制的位置     被复制的数量

        //由于下标后的都往后移动了一位,因此index的位置为空,插进去即可
        elements[index]=element;
        size++;
    }

    /**
     * 检查下标是否越界
     * @param index
     */
    private void rangeCheck(int index) {
        if (index>size||index<0){//若插入的下标比数据实际数量大或者输入的是个不合常理的负数
            throw new IndexOutOfBoundsException((outOfBoundsMsg(index)));//抛出异常并返回要插入的下标数和实际数据的数量
        }
    }

    /**
     * 用于越界异常的信息抛出
     * @param index 输入下标
     * @return 返回下标和实际数据的数量大小
     */
    private String outOfBoundsMsg(int index) {
        return "Index:"+index+",Size:"+size;
    }

    /**
     * 清空列表所有元素
     * 由垃圾回收器(GC)进行回收
     */
    public void clear(){
        //记录对象修改次数
        modCount++;
        //让数组所有的数据都变为null, clear to let GC do its work
        for (int i=0;i<size;i++){
            elements[i]=null;
        }
        //这时长度都为0了
        size=0;
    }

    /**
     * 根据下标更改数据
     * @param index 下标
     * @param element 想要更改的数据
     * @return 返回原始数据
     */
    public E set(int index, E element) {
        //首先判断是否越界
        rangeCheck(index);

        E oldValue= (E) elements[index];
        elements[index]=element;
        return oldValue;
    }

    /**
     * 检查操作数是否一致,防止并发修改错误
     *//*
    private void checkForComodification() {
        if (MyArrayList.this.modCount != this.modCount)//线程中的和保存的数据不一样
            throw new ConcurrentModificationException();//不一致则抛出异常
    }*/

    /**
     * 简简单单的根据下标查询数据操作
     * @param index 下标
     * @return 返回数据信息
     */
    public E get(int index) {
        rangeCheck(index);
        return (E) elements[index];
    }

    /**
     * 根据下标删除元素
     * @param index  要删除的下标
     * @return 返回要删除的数据
     */
    public E remove (int index){
        rangeCheck(index);//检查一下下标是否合适

        modCount++;//操作数加一
        E oldValue= (E) elements[index];

        //定义numMoved,记录一下index后面的数,等会要把他们都移动了
        int numMoved=size-index-1;
        if (numMoved>0){
            System.arraycopy(elements,index+1,elements,index,numMoved);//通过本地静态的复制方法,来实现数据的前移。
        }
        elements[size]=null;//都前移了以后原本最后一位数的内存设为null,让GC回收了 clear to let GC do its work
        size--;
        return oldValue;
    }

    /**
     * 找到相应的元素并对其进行删除
     * @param o
     * @return 返回true表示删除完成,false表示删除失败
     */
    public boolean remove(Object o) {
        if (o == null) {//删除null数据
            for (int index = 0; index < size; index++)
                if (elements[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {//删除正常的数据
            for (int index = 0; index < size; index++)
                if (o.equals(elements[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;//若没找到该数据则返回false
    }

    /**
     * 无需进行判断下标是否合适的快速删除方法
     * @param index
     */
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elements, index+1, elements, index,
                    numMoved);
        elements[--size] = null; // clear to let GC do its work
    }

    /**
     * 判断数据是否为空(列表里没数据)
     * @return 若为空则返回true
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 查询是否有该数据
     * @param o
     * @return 若有返回true
     */
    public boolean contains(Object o) {
        return indexOf(o) >= 0;//若该索引为正,则表明有该数据,且第一次出现的位置在indexOf
    }

    /**
     *返回指定元素的第一次出现的索引
     * @param o
     * @return 若存在则返回第一次出现的位置,若没有则返回-1
     */
    public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elements[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elements[i]))
                    return i;
        }
        return -1;
    }

    /**
     *返回指定元素的最后一次出现的索引
     * @param o
     * @return 若存在则返回最后一次出现的位置,若没有则返回-1
     */
    public int lastIndexOf(Object o) {
        if (o == null) {
            for (int i = size-1; i >= 0; i--)//从后向前遍历即可
                if (elements[i]==null)
                    return i;
        } else {
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elements[i]))
                    return i;
        }
        return -1;
    }

    @Override
    public String toString() {
        return "MyArrayList{" +
                "elements=" + Arrays.toString(elements) +
                '}';
    }
}

posted @ 2021-02-03 20:15  牛家俊  阅读(66)  评论(0编辑  收藏  举报