Java集合之ArrayList

1 篇文章 0 订阅
1 篇文章 0 订阅

底层数据结构

ArrayList的底层数据结构是数组

transient Object[] elementData;//底层数据结构,数组

private int size;//已存放数量

这里先说一下ArrayList中大量使用的复制数组的方法,该方法是一个native方法,jvm进行了特殊的优化,比Java for循环复制效率高

public static native void arraycopy(Object src,  // 源数组
                                    int  srcPos, //开始复制的位置
                                    Object dest, //目标数组
                                    int destPos, //复制到目标数组的开始位置
                                    int length); //复制的个数

构造函数

使用无参构造函数时,底层数组指向默认的空数组

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//{}
}

按容量初始化数组

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

复制已有集合,参数为null会报NullPointerException

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

add(E e)

add(E e)ArrayList的核心方法之一,该方法把元素插入List末尾,实现:

  • 保证数组能容纳新元素,在数组不能容纳新元素时进行扩容
  • 把元素插入末尾
public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

扩容的核心代码

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // = oldCapacity + oldCapacity/2 = oldCapacity * 1.5,扩容后容量为原来的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 把数据复制到新数组里,最终调用了 System.arraycopy();
    elementData = Arrays.copyOf(elementData, newCapacity);
}

add(int index, E e)

在指定位置插入元素,实现

  • 检查index
  • 必要时扩容
  • index之后的元素后移,在index处插入元素
public void add(int index, E element) {
    rangeCheckForAdd(index);//检查index
    ensureCapacityInternal(size + 1);  // 必要时扩容
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);//把index之后的元素后移
    elementData[index] = element;//在index处插入元素
    size++;
}

示意图

ArrayList插入元素

remove(int index)

删除指定位置的元素,实现:

  • 检查index
  • index之后的元素前移,index上的元素被覆盖,把删除后一个元素(最后一个元素重复了)
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;
    return oldValue;
}

示意图

ArrayList删除元素

ConcurrentModificationException

运行以下代码,会出现异常 java.util.ConcurrentModificationException

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    list.add("d");
    for (String s : list) {
        list.remove(s);
    }
}

这是因为foreach是一个语法糖,真正的内部实现为

for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
    String s = it.next();
    list.remove(s);
}

而在使用Iterator时不能使用Iterator.remove() Iterator.add(obj) 之外的方法对list进行增删,否则就会抛出ConcurrentModificationException

正确写法

  • 使用Iterator.remove()

  • 使用 Java8 新增方法 removeIf()

    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }
    

subList()

运行这段代码会出现异常ClassCastException

public static void main(String[] args) {
    List<String> list = new ArrayList<>();
    list.add("a");
    ArrayList<String> arrayList = (ArrayList<String>) list.subList(0, 1);
}

这里的原因《阿里巴巴 Java 开发手册》也说得很清楚了

  1. 【强制】 ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常,即 java.util.ArrayList$SubList cannot be cast to java.util.ArrayList。
    说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList 而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上

  2. 【强制】在 subList 场景中,高度注意对原集合元素的增加或删除,均会导致子列表的遍历、 增加、删除产生 ConcurrentModificationException 异常。

小结

  • ArrayList底层数据结构是数组
  • 插入删除需要移动元素(末尾除外)
  • 扩容后容量为原来的1.5倍
  • ConcurrentModificationException
  • subList
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值