ArrayList的理解(小白版)

目录

一、属性

二、构造方法

三、基础测试用例

四、add()方法

1.add无参数的方法

2.add有参数的方法

五、grow()方法【扩容】

六、remove()方法

1.删除指定位置上的元素

2.删除指定的元素且索引最小

七、indexOf()和LastIndexOf()方法

1.从前往后遍历indexOf()

2.从后往前遍历lastIndexOf()方法


看到源码时,先看属性,再看构造方法,再看一些基础方法

ArrayList有三种遍历方式:迭代器遍历、索引遍历、for循环遍历ArrayList是基于数组实现的,一个动态数组,其容量能自动扩大,动态扩容

一、属性

 private static final int DEFAULT_CAPACITY = 10;
    //需要存放数据时定义的初始化数组的大小
​
//why要弄两个空数组呢?
    private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    //定义默认空数组
    /*存储数组列表元素的数组缓冲区。ArrayList的容量就是这个数组缓冲区的长度。
    当添加第一个元素时,任何具有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空            ArrayList将    被扩展为DEFAULT_CAPACITY。*/
​
    transient Object[] elementData;
    //存储ArrayList内的元素,存放数据的数组(不能被序列化)
    private int size;
    /*数组列表的大小(它包含的元素的数量)。*/

二、构造方法

/*构造一个具有指定初始容量的空列表。
    形参:initialCapacity -列表的初始容量
    抛出:IllegalArgumentException -如果指定的初始容量为负值*/
//有参构造,initialCapacity为传入的数组长度
    public MyArraylist(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
//为什么等于0要单独做个条件语句
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            //这是抛出异常机制,用来判断输入的数据是否合理
            throw new IllegalArgumentException("Illegal Capacity: "+
                    initialCapacity);
        }
    }
​
    /*构造一个初始容量为10的空列表。*/
    public MyArraylist() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    /*这是无参构造,如果我们创建ArrayList对象时没有传入参数,
     * 就会调用该方法创建ArrayList对象,这个对象的elementData被初始化为空的Object[]
     * 空的Object[]会给默认容量10,
     * 在第一次调用add时,elementData将会变成默认的长度:10*/

三、基础测试用例

这里我用的是无参构造。有参构造就是直接开辟了一块连续的空间,但增添元素容量不够时,系统会自动扩容

MyArraylist myArraylist = new MyArraylist(13);

传个参数就ok

package com.two.me;
import com.one.www.MyArraylist;
public class Test03 {
    public static void main(String[] args) {
        MyArraylist myArraylist = new MyArraylist();
        myArraylist.add(10);
        myArraylist.add(9);
        myArraylist.add(8);
        myArraylist.add(7);
        myArraylist.add(6);
        myArraylist.add(5);
        myArraylist.add(4);
        myArraylist.add(3);
        myArraylist.add(2);
        myArraylist.add(1);
        myArraylist.add(0);
    }
}

四、add()方法

1.add无参数的方法

直接将元素增至列表末尾,当增加第一个元素时,element容量从0增至默认初始容量10,在扩容时实现扩容实现步骤:

add()——>ensureCapacityInternal()【判断数组容量】——>calculateCapacity()【计算当前数组的容量,并进行恰当的赋值】——>ensureExplicitCapacity()【判断是否需要进行扩容】——>.....

/*将指定的元素追加到列表的末尾。
        形参:e -元素附加到这个列表
        返回值:true (Collection.add来指定*/
    public boolean add(Object e) {//
        //确认list容量,如果不够,容量加1。注意:只加1,保证资源不被浪费
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //将元素放在size位置上
        elementData[size++] = e;//先运算后自加
        return true;
    }
​
//确保内部容量足够,检查数组容量,不够就进行扩容
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
​
//主要是第一次无参构造时,将数组默认容量赋为10,返回最小容量
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            //第一次调用add(1)时,elementData为空数组
            return Math.max(DEFAULT_CAPACITY, minCapacity);//这里调用了Math的max方法
        }
        return minCapacity;//返回最小容量
    }
​
//这里就是来判断是否需要扩容的方法
    private void ensureExplicitCapacity(int minCapacity) {
        //
​
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            //如果所需的最小容量大于当前数组的长度
            //意思就是,你当前数组长度为0,而最小容量为10,当前数组就需要扩容
    
            grow(minCapacity);
    }

2.add有参数的方法

//add(索引,添加的元素)
    public void add(int index, Object element) {
        rangeCheckForAdd(index);//检查索引是否越界
​
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                size - index);
        /*调用arraycopy方法,将[index,size]的元素均后移一位*/
        elementData[index] = element;//将index上的元素变为element
        size++;//记得将数组的索引加1
    }
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            //如果索引大于数组的索引或者索引小于0,就抛出异常
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    private String outOfBoundsMsg(int index) {//解释异常原因,方便知道哪错了
        return "Index: "+index+", Size: "+size;
    }

例:抛出异常

基础测试用例+
    myArraylist.print();
myArraylist.add(12,45);
myArraylist.print();

五、grow()方法【扩容】

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;//oldCapacity为老数组长度
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //这个应该是位运算符吧,当当前数组容量为0时,newCapacity也为0?
        //开始扩容了,新的容量=当前容量+当前容量/2,即将当前容量增加一半(当前容量增加1.5倍)
        if (newCapacity - minCapacity < 0)
            //如果扩容后的容量还是小于想要的最小容量
            //将扩容后的容量再次扩容为想要的最小容量
//为什么不直接将数组容量扩到自己想要的容量呢?
            newCapacity = minCapacity;
//int newCapacity = minCapacity;//这个扩容是刚刚合适的,而上面的方式扩了还有多
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            //如果扩容后的容量大于临界值,就进行大容量分配
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
        //copyOf(原数组,新的数组长度)
    }
​
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            //如果minCapacity小于0,抛出异常
            throw new OutOfMemoryError();
        //如果想要的容量大于MAX_ARRAY_SIZE,则分配Integer.MAX_VALUE,否则分配MAX_ARRAY_SIZE
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }
  1. 上述例子输出:

  2. 当将扩容方式改为

    int newCapacity = minCapacity;

    输出为:

因此,第一种扩容方式是扩大数组的多少倍,扩大之后还有多,第二种方式是刚好满足需求的。这里暂时不细细理解hugeCapacity()方法。

六、remove()方法

1.删除指定位置上的元素

 //删除指定位置上的元素
    public Object remove(int index) {
        rangeCheck(index);//检查索引是否越界
        Object oldValue = elementData(index);//记下索引上的元素
//既然是记下索引上的元素,为什么不这样呢?
//Object oldValue = elementData[index];
        int numMoved = size - index - 1;
        //记下应该左移的元素个数,size是数组元素个数,而非索引
        if (numMoved > 0)
            //如果需要左移的话
            System.arraycopy(elementData, index+1, elementData, index,
                    numMoved);//数组左移
        elementData[--size] = null; // clear to let GC do its work
        //size先自减再使用,然后将索引为size-1处的元素置为null。
        // 为了让GC起作用,必须显式的为最后一个位置赋null值
        return oldValue;
        //返回被删除的元素
    }
​
    private void rangeCheck(int index) {
        if (index >= size)
//怎么=也抛错
//因为此时size为数组元素的个数
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    Object elementData(int index) {
        return  elementData[index];
    }
  1. 上述例子输出为:与2相同

  2. 如果是 Object oldValue = elementData[index];

    基础用例+
        System.out.println("原列表:");
        myArraylist.print();
        System.out.println("在某个索引插入某个数:");     
        myArraylist.add(8,45);
        myArraylist.print();
        System.out.println("移除某个索引上的元素:");
        myArraylist.remove(3);
        myArraylist.print();

    输出为:

2.删除指定的元素且索引最小

    //删除指定的元素且索引最小
    public boolean remove(Object o, int a) {
//因为这个方法与上一个remove方法类似,不好区分,于是来个形参a做个区别的标志
        if (o == null) {//如果对象为空
//why还要判断是不是为空?直接循环不就好了,if的意义何在?
//删了这个if条件语句,
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    //找到了第一个与之匹配的,就进行删除操作
                    return true;
                }
        }
        return false;
    }
​
    //进行删除的方法
    private void fastRemove(int index) {
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                    numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
  1. 上述代码输出:

    基础测试用例+
        System.out.println("原列表:");
        myArraylist.print();
        System.out.println("移除某个元素且其索引最小:");
        myArraylist.remove(4,1);//若将4换成43,列表不变
        myArraylist.print();

    输出为:

  2. 将代码改为下述代码,其结果与1相同

    public boolean remove(Object o, int a) {
                for (int index = 0; index < size; index++)
                    if (o.equals(elementData[index])) {
                        fastRemove(index);
                        //找到了第一个与之匹配的,就进行删除操作
                        return true;
                        //只执行一个return语句,且执行完return语句后不再执行其他语句
                    }
            return false;
        }

七、indexOf()和LastIndexOf()方法

1.从前往后遍历indexOf()

    //从前往后遍历
    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;//如果遍历完数组后都没有发现指定元素,就返回-1
    }
//将上述代码改为下述代码其运行结果一样
    /*public int indexOf(Object o) {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;//返回索引大小
        return -1;//如果遍历完数组后都没有发现指定元素,就返回-1
    }*/
/*测试用例:【将add(8)改为add(4)】
    基础测试用例+
    System.out.println("原列表:");
     myArraylist.print();
     System.out.println("遍历某个元素且其索引最小:");
     System.out.println(myArraylist.indexOf(4));*/

输出为:

2.从后往前遍历lastIndexOf()方法

//这是从后往前遍历,其返回的是最大索引
    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;
    }
//代码可去if条件语句,去了if后的结果一样
/*测试用例:【将add(8)改为add(4)】
    基础测试用例+
    System.out.println("原列表:");
     myArraylist.print();
     System.out.println("遍历某个元素且其索引最大:");
        System.out.println(myArraylist.lastIndexOf(4));*/
//结果为6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值