java中ArrayList类的理解

ArrayList介绍

List接口继承了集合类中的基本接口Collection接口。list存储有序的,可重复的数据,也被称之为动态数组。而ArayList类就是list接口的实现类之一。
来一个简单的图示:
在这里插入图片描述

语法格式:

ArrayList<数据类型> 变量名 = new ArrayList<数据类型>();

“<数据类型>”表示泛型,限定了存储在集合中元素的数据类型,可以自行定义。
主要特点:
ArrayList作为list接口的主要实现类,是线程不安全的,但是效率高。

ArrayList类的常用方法及源码理解

ArrayList在底层使用Object[] elementData数组存储。

 transient Object[] elementData;

1.构造方法

  • public ArrayList()
    空参构造方法
 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

这里的DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个Object类型的数组,值被赋值了一个{},这样的好处就是当ArrayList对象被实例化之后,并没有第一时间将数组实例化。在某些场景中节省了一定时间内的内存。

  1. public ArrayList(int initialCapacity)
 public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

如果参数 > 0 则底层创建一个给定长度的数组。如果等于零,则与空参构造器无异,小于零就会抛出IllegalArgumentException类型的异常。如果确定数据的长度的话,可以直接使用带参的构造器,就会更少的用到扩容操作,效率就要高一些。

2.size() 方法

 public int size() {
        return size;
    }

在每一个实例化的ArrayList对象中都有一个私有的size成员变量,用来记录数组的列表的长度。

3.add(Object element)
向列表的尾部添加指定的元素

  public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

由于底层使用数组实现,所以每次添加元素的时候都需要判断,size+1是否大于数组的长度,如果大于的话,就需要对数组进行扩容操作。因为底层会自动扩容,而不需要人为的去判断数组的容量是否够用。相较于LinkedList底层用双向链表,ArrayList可谓是名副其实的动态数组了。

4.get(int index)
返回列表中指定位置的元素,index从0开始

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

首先检查下标index的大小,如果大于数组长度的话,就会抛出我们熟悉的IndexOutOfBoundsException下标越界异常。然后返回的方式和数组一样,通过下标返回数组中元素return (E) elementData[index];

5.clear()
从列表中移除所有元素

public void clear() {
        modCount++;
        for (int i = 0; i < size; i++)
            elementData[i] = null;
        size = 0;
    }

modCount是一个transient类型(即不会被序列化)的整型数据,用于记录这个列表结构被修改的次数,然后遍历底层数组,将所有元素都赋null,同时将size赋值为0

6.isEmpty()
判断列表是否包含元素,不包含元素则返回 true,否则返回false

   public boolean isEmpty() {
        return size == 0;
    }

源码就是判断Size是否等于零

7.remove(int index)
移除列表中指定索引位置(从0开始)的元素,并返回被删元素,同时后面元素向前移动

  public E remove(int index) {
        rangeCheck(index);
        modCount++;
        E oldValue = elementData(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

        return oldValue;
    }

首先检查下标是否越界,然后记录列表改变的modCount++,记录下当前下标对应的数组值,然后求出从index到数组末尾共有多少个元素,如果>0(即当前下标对应的不是数组最后一个元素),则通过System中提供了一个native的静态方法arraycopy()将数组后面部分,往前复制。

8.remove(Object o)
移除集合中第一次出现的指定元素,移除成功返回true,否则返回false

 public boolean remove(Object o) {
        if (o == null) {
            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;
    }

首先判断参数是否为空,如果为空的话,遍历数组,当遇到第一个为空的元素时,就通过Index下标,和上述一样通过下标将为空的元素移除。
然后如果不为空的话,同样遍历数组 ,使用Object类中的equals方法,也就是== 来判断是否相等,如果相等的话一样通过下标Index,将数组重新整理。
注意:两种情况的for循环都是满足 条件直接return,所以是找第一个。

9.contains(Object o)
如果列表包含指定的元素,则返回 true

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

for()循环遍历数组,然后判断参数是否和数组中的某个元素相等,如果相等返回下标,否则返回-1

10.add(int index, Object element)
在列表的指定位置插入指定元素,原来位置元素后置

 public void add(int index, E element) {
        rangeCheckForAdd(index);
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

第一步判断index是否在0到size之间,不是的话抛出异常,第二步,判断size+1之后是否超出了底层数组长度的最大值,如果超出则需要进行数组的扩容,然后就是利用System中提供的native的静态方法arraycopy()把从index到size的内容全部往数组后复制,最后将index位置填入新增的数据,Size++。

11.set(int i, Object element)
将索引 i 位置元素替换为元素element并返回被替换的元素

public E set(int index, E element) {
        rangeCheck(index);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

第一步检查下标是否越界,第二步将数组中本来index位置初的元素保存,第三步替换,第四步返回,和我们自己写的数组元素替换没啥区别。

扩容操作

 private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

扩容步骤:
1.记录下底层数组的长度
2.设置一个新的长度,为原来的1.5倍。
3. 如果扩容之后的大小与需要的大小相比,还是小了,就直接拿需要的长度
4.如果需要的容量比底层设置的阈值,就会把新的长度设置为整形的最大值,如果还超过的话,就报error了
5.根据新的长度创建一个数组,然后把旧的数组中的数据copy入新的数组

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小黑cc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值