java基础-- List ArrayList

本文详细介绍了Java中ArrayList的工作原理,包括其内部存储结构、基本操作方法如add、set、get、remove等的实现细节,并探讨了多线程环境下的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

List是作为日常开发中常用到的一个接口,ArrayList是List的实现之一。

ArrayList 继承了AbstractList类(还实现了RandomAccess, Cloneable,ava.io.Serializable等接口)。它的存储
结构是数组,有一个 int类型的size field作为存储数组对象的索引。

对于List接口我们一般常用到的方法如下:
add,set,get,remove(int index),remove(E value),sort,toArray等

注:使用空的构造函数生成的ArrayList中存储数据的数组的初始长度为10

add方法:该方法有2个实现,分别如下

  public boolean add(E e) {
        /** 
         * size+1为添加后的数组索引,用该值去计算当前数组是否能够继续存储需要
         * 添加的值,如果数组长度不够的话,将会生成一个新的的数组(长度为原来数组的1.5倍,这个是jdk1.8的实现)
         **/
        ensureCapacityInternal(size + 1);  
        //elementData就是存放数据的数组
        elementData[size++] = e;
        return true;
    }

    public void add(int index, E element) {
        //检查 index是否是否超过 size或者小于0,如果是,则跑出异常
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        /**
         *  这个方法是native的(JNI)
         *  Object src :  原数组
         *  int srcPos :  从元数据的起始位置开始
         *  Object dest :目标数组
         *  int destPos : 目标数组的开始起始位置
         *  int length  : 要copy的数组的长度
         *  具体可以参考 https://www.cnblogs.com/DeepLearing/p/5688555.html
         **/
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        //覆盖当前位置的数据                 
        elementData[index] = element;
        size++;
    }    

set方法:

 public E set(int index, E element) {
        //检查index是否合法,不合法则抛出IndexOutOfBoundsException异常
        rangeCheck(index);

        E oldValue = elementData(index);
        //覆盖
        elementData[index] = element;
        return oldValue;
    }

get方法比较简单,就是获取之前会判断一下传入参数是否越界,然后在放回数据之前做一个泛型的类型转换

 public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

remove方法

 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;
    }

/**
 * 这个方法多做了一个查找的动作,移除的时候调用了内部的 fastRemove方法
 * fastRemove 方法和remove()方式类似,也是用了 System.arraycop
 **/
 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;
    }

    /*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
    private void fastRemove(int index) {
        modCount++;
        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
    }

sort方法,需要传入一个Comparator接口的实现,在ArrayList的内部是调用了Arrays.sort方法,这边有一个需要注意的是,
因为ArrayList是非线程安全的,所以在多线程环境下sort方法有可能会抛出 ConcurrentModificationException异常。所以多线程环境可以使用 Collections.synchronizedList(new ArrayList()); 或者是用java.util.concurrent(java8)包下的集合类

    @Override
    @SuppressWarnings("unchecked")
    public void sort(Comparator<? super E> c) {
        //记录当前操作次数的标记值
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        //如果在排序过程中有其他线程操作了这个list将会跑出异常
        //注:modCount这个成员变量在除了add,get,set等操作外的每一次操作ArrayList的时候都会增加一次
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

toArray 转化为数组,这个方法也有2个实现,关键的是他不是直接放回内部的存储数据的数组,而是做了一次值拷贝

 public Object[] toArray() {
        return Arrays.copyOf(elementData, size);
    }

 public <T> T[] toArray(T[] a) {
        if (a.length < size)
            // Make a new array of a's runtime type, but my contents:
            return (T[]) Arrays.copyOf(elementData, size, a.getClass());
        System.arraycopy(elementData, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    }    

补充:
indexOf,lastIndexOf这2个方法也很常用,这2个方法的实现其实就是将ArrayList中的数组遍历一次,不过indexOf是从数组开头开始遍历,而lastIndexOf是从当前数组下标索引size-1的位置开始往前查找。

注:该内容基于jdk1.8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值