ArrayList----源码分析

源码中的简介:

List接口的可调整数组实现。实现所有可选列表操作,并允许所有元素,包括null。除了实现List接口之外,这个类还提供了一些方法来操作内部用于存储列表的数组的大小。(这个类大致相当于Vector,只是它是不同步的。)size、isEmpty、get、set、iterator和listiterator操作在常量时间内运行。添加操作在平摊常数时间内运行,即添加n个元素需要O(n)时间。所有其他操作都在线性时间内运行(粗略地说)。与LinkedList实现相比,常数因子较低。每个ArrayList实例都有一个容量。容量是用于存储列表中元素的数组的大小。它总是至少和列表大小一样大。

当元素被添加到ArrayList中时,它的容量会自动增长。除了添加一个元素具有恒定的平摊时间成本这一事实之外,没有指定增长策略的细节。应用程序可以在添加大量元素之前使用ensureCapacity操作来增加ArrayList实例的容量。这可能会减少增量再分配的数量。注意,这个实现是不同步的。

如果多个线程同时访问一个ArrayList实例,并且至少有一个线程在结构上修改了该列表,则必须在外部进行同步。(结构修改是任何添加或删除一个或多个元素的操作,或者显式地调整后台数组的大小;仅仅设置元素的值并不是对结构的修改。)这通常是通过对一些自然封装列表的对象进行同步来完成的。如果不存在这样的对象,则应该使用集合“包装”列表。synchronizedList方法。这最好在创建时完成,以防止对列表的意外非同步访问:list list = Collections。synchronizedList(新ArrayList(…));

这个类的iterator和listtiterator方法返回的迭代器是快速失败的:如果在迭代器创建后的任何时间,以除通过迭代器自己的remove或add方法之外的任何方式修改列表,迭代器将抛出ConcurrentModificationException。因此,在面对并发修改时,迭代器会快速而干净地失败,而不是在未来不确定的时间冒任意的、不确定的行为的风险。请注意,不能保证迭代器的快速故障行为,因为一般来说,在存在非同步并发修改的情况下不可能做出任何硬保证。快速失败迭代器在尽最大努力的基础上抛出ConcurrentModificationException。因此,编写依赖于此异常的程序以确保其正确性是错误的:迭代器的快速失败行为应该仅用于检测错误。

继承与实现
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
​
  }

ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。

ArrayList 继承于 AbstractList ,实现了 List, RandomAccess, Cloneable, java.io.Serializable 这些接口。

List : 表明它是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问。

  • RandomAccess :这是一个标志接口,表明实现这个接口的 List 集合是支持 快速随机访问 的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。

  • Cloneable :表明它具有拷贝能力,可以进行深拷贝或浅拷贝操作。

  • Serializable : 表明它可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输,非常方便。

ArrayList 类图

ArrayList 类图

ArrayList 和 Vector 的区别?(了解即可)
  • ArrayListList 的主要实现类,底层使用 Object[]存储,适用于频繁的查找工作,线程不安全 。

  • VectorList 的古老实现类,底层使用Object[] 存储,线程安全。

Arraylist 与 LinkedList 区别?
  • 是否保证线程安全: ArrayListLinkedList 都是不同步的,也就是不保证线程安全;

  • 底层数据结构: ArrayList 底层使用的是 Object 数组LinkedList 底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)

  • 插入和删除是否受元素位置的影响:

    • ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element)),时间复杂度就为 O(n)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。

    • LinkedList 采用链表存储,所以在头尾插入或者删除元素不受元素位置的影响(add(E e)addFirst(E e)addLast(E e)removeFirst()removeLast()),时间复杂度为 O(1),如果是要在指定位置 i 插入和删除元素的话(add(int index, E element)remove(Object o),remove(int index)), 时间复杂度为 O(n) ,因为需要先移动到指定位置再插入和删除。

  • 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList(实现了 RandomAccess 接口) 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。

  • 内存空间占用: ArrayList 的空间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

ArrayList扩容(重点)

当我们初始化数组的时候,可以指定初始容量,如果没有选择传入值去初始化容量时,默认容量是10,当我们在不断使用add()方法添加新数据时,如果超过十这个容量时,就会触发扩容机制,在这个时候,我们回去获取当前的容量大小,将其值进行右移一位(实际上对于正数而言就是除以2)在加上原来的容量大小,此时相较于旧容量增长了1.5倍,将这个容量作为新object数组,使用Arrays类中的copyOf()方法将我们的旧数组到新数组中去。

 /**
     * ArrayList扩容的核心方法。
     */
    private void grow(int minCapacity) {
        // oldCapacity为旧容量,newCapacity为新容量
        int oldCapacity = elementData.length;
        //将oldCapacity 右移一位,其效果相当于oldCapacity /2,
        //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //再检查新容量是否超出了ArrayList所定义的最大容量,
        //若超出了,则调用hugeCapacity()来比较minCapacity和 MAX_ARRAY_SIZE,
        //如果minCapacity大于MAX_ARRAY_SIZE,则新容量则为Integer.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE。
        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);
    }
arraylist中的克隆默认为浅克隆,如何深克隆?

// 使用流克隆ArrayList

ArrayList<Dog> clonedList = dogs.stream() .map(Dog::clone).collect(Collectors.toList());

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值