Java数据存储之---ArrayList

11 篇文章 1 订阅

官方文档描述

可调整大小的数组实现了List的接口。实现所有可选列表操作,并允许所有元素包括null。除了实现List接口, 这个类提供了操作数组大小的方法在内部用于存储列表。(这个类大致相当于
 Vector,除了它是不同步的。)这些操作size,isEmpty,get,set,iterator,listIterator操作在固定的常数时间内运行,add操作在摊销的常数时间内运行,也就是说添加 n 个元素需要 O(n) 时间。所有其他操作以线性时间运行(粗略地说),与 LinkedList实现相比,常数因子较低。每个 ArrayList 实例都有一个容量,容量是用于存储列表中元素的数组的大小。它总是至少与列表大小一样大。随着元素被添加到 ArrayList,它的容量会自动增长。除了添加一个元素具有恒定的摊销时间成本这一事实之外,没有指定增长策略的细节。应用程序可以在添加大量元素之前使用 ensureCapacity 增加 ArrayList实例的容量。这可以减少增量重新分配的数量。请注意,此实现不是同步的。

如果多个线程同时访问一个 ArrayList实例, 并且至少有一个线程在结构上修改了列表,它
 必须在外部同步。 (结构修改是添加或删除一个或多个元素,或显式调整后备数组大小的任何操作;仅设置元素的值不是结构修改。)这通常通过同步一些自然封装的对象来完成列表。

如果不存在这样的对象,则应使用

{@link Collections#synchronizedList Collections.synchronizedList}

方法。这最好在创建时完成,以防止意外对列表的非同步访问:

 List list = Collections.synchronizedList(new ArrayList(...));

此类的 {@link #iterator() 迭代器} 返回的迭代器和 {@link #listIterator(int) listIterator} 方法是快速失败。如果列表在迭代器之后的任何时间都在结构上被修改,以任何方式,除了通过迭代器自己的{@link ListIterator#remove() remove} {@link ListIterator#add(Object) add} 方法,迭代器将抛出 {@link ConcurrentModificationException} 因此,面对并发修改,迭代器快速而干净地失败,而不是在未来不确定的时间冒任意的、非确定性的行为。

请注意,不能保证迭代器的快速失败行为,因为一般来说,在存在不同步的并发修改的情况下,不可能做出任何硬保证。 快速失败的迭代器会尽最大努力抛出 {@code ConcurrentModificationException}。因此,编写一个依赖于这个异常的正确性的程序是错误的:迭代器的快速失败行为应该只用于检测错误。

类初始化

/**
 * 默认初始容量
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 用于空实例的共享空数组实例
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 用于默认大小的空实例的共享空数组实例
 * 将其与 EMPTY_ELEMENTDATA 区分开来,以了解添加第一个元素时要扩充多少
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存储 ArrayList元素的数组缓冲区。
 * ArrayList的容量就是这个数组缓冲区的长度。 
 * 任何带有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList添加第一个元素时将扩展 为 DEFAULT_CAPACITY
 */
// Android-note: 也可以从 java.util.Collections 访问
transient Object[] elementData; // 非私有以简化嵌套类访问

/**
 * ArrayList 的大小(它包含的元素数量)。
 */
private int size;

/**
 * 构造一个具有指定初始容量的空列表。
 *
 * @param  initialCapacity  列表的初始容量
 * @throws IllegalArgumentException 如果指定的初始容量为负
 */
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);
    }
}

/**
 * 构造一个初始容量为 10 的空列表。
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * 按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。
 *
 * @param c 将其元素放入此列表的集合
 * @throws NullPointerException 如果指定的集合为空
 */
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

容量相关

/**
 * 如有必要,增加此 <tt>ArrayList</tt> 实例的容量
 * 以确保它至少可以容纳最小容量参数指定的元素数量
 *
 * @param   minCapacity   所需的最小容量
 */
public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTD
        // 如果不是默认元素表,则为任何大小
        ? 0
        // 大于默认空表的默认值, 它应该是默认大小
        : DEFAULT_CAPACITY;
    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // 考虑到溢出的情况
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

/**
 * 要分配的数组的最大大小
 * 一些 VM 在数组中保留一些标题字
 * 尝试分配更大的数组可能会导致
 * OutOfMemoryError:请求的数组大小超过 VM 限制
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
 * 增加容量以确保它至少可以容纳最小容量参数指定的元素数量
 *
 * @param minCapacity 所需的最小容量
 */
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity 通常接近 size
    elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) //溢出
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

重要方法

/**
 * 返回此列表中的元素个数
 */
public int size() {
    return size;
}

/**
 * 根据size判断列表是否为空
 */
public boolean isEmpty() {
    return size == 0;
}

/**
 * p判断是否包含某一个元素,使用indexOf方法完成,如果返回-1表示没有包含该元素
 */
public boolean contains(Object o) {
    return indexOf(o) >= 0;
}

/**
 * 返回此列表中指定元素第一次出现的索引,如果此列表不包含该元素,则返回 -1
 */
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]))//遍历缓存数组elementData,找到相等的元素
                return i;
    }
    return -1;
}

/**
 * 返回最后一个指定的元素
 */
public int lastIndexOf(Object o) {//遍历从缓存数组elementData的尾部开始
    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;
}

/**
 * 返回ArrayList 实例的浅表副本
 *(元素本身不会被复制)
 */
public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

/**
 * 以正确的顺序(从第一个元素到最后一个元素)返回包含此列表中所有元素的数组
 * 返回的数组将是“安全的”,因为没有对它的引用由这个列表维护
 * (换句话说,这个方法必须分配一个新数组)
 * 因此,调用者可以自由修改返回的数组
 * 此方法充当基于数组的 API 和基于集合的 API 之间的桥梁。
 */
public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

/**
 * 以正确的顺序(从第一个元素到最后一个元素)返回一个包含此列表中所有元素的数组
 * 返回数组的运行时类型是指定数组的运行时类型
 * 如果列表适合指定的数组,则在其中返回
 * 否则,将使用指定数组的运行时类型和此列表的大小分配一个新数组
 *
 * 如果列表适合指定的数组并有剩余空间(即数组的元素比列表多)
 * 则数组中紧跟集合末尾的元素设置为null
 *(如果调用者知道列表不包含任何空元素,这对于确定列表的长度only很有用。)
 *
 * @param a 存储列表元素的数组(如果它足够大);否则,将为此目的分配相同运行时类型的新数组。
 * @return 包含列表元素的数组
 * @throws ArrayStoreException 如果指定数组的运行时类型不是此列表中每个元素的运行时类型的超类型
 * @throws NullPointerException 如果指定的数组为空
 */
@SuppressWarnings("unchecked")
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;
}

/**
 * 返回此列表中指定位置的元素。
 *
 * @param  index 要返回的元素的索引
 * @return 此列表中指定位置的元素
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E get(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));//索引超过列表长度
    return (E) elementData[index];
}

/**
 * 将此列表中指定位置的元素替换为指定元素
 *
 * @param index 要替换的元素的索引
 * @param element 要存储在指定位置的元素
 * @return 先前在指定位置的元素
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E set(int index, E element) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));//索引超过列表长度
    E oldValue = (E) elementData[index];
    elementData[index] = element;
    return oldValue;
}

/**
 * 将指定元素附加到此列表的末尾。
 *
 * @param e 要附加到此列表的元素
 * @return true (如指定的 {@link Collection#add})
 */
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 增加 modCount!!
    elementData[size++] = e;
    return true;
}

/**
 * 在此列表中的指定位置插入指定元素
 * 将当前位于该位置的元素(如果有)和任何后续元素向右移动(将其索引加一)
 *
 * @param index 要插入指定元素的索引
 * @param element 要插入的元素
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public void add(int index, E element) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    ensureCapacityInternal(size + 1);  // 增加 modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

/**
 * 移除此列表中指定位置的元素
 * 将任何后续元素向左移动(从它们的索引中减去 1)
 *
 * @param index 要删除的元素的索引
 * @return 从列表中删除的元素
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public E remove(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    modCount++;
    E oldValue = (E) elementData[index];
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // 置空,则会被内存(内存管理GC)回收
    return oldValue;
}

/**
 * 从此列表中删除第一次出现的指定元素(如果存在)
 * 如果列表不包含该元素,则它是不变的。
 * 更正式地说,删除具有最低索引 i 的元素
 * 使得 (o==null&nbsp;&nbsp;get(i)==null&nbsp;:&nbsp;o.equals(get(i)))(如果存在这样的元素)
 * 如果此列表包含指定元素(或等效地,如果此列表因调用而更改),则返回true。
 * 
 * @param o 要从此列表中删除的元素(如果存在)
 * @return true如果此列表包含指定的元素
 */
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 void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    elementData[--size] = null; // 置空,则会被内存(内存管理GC)回收
}

/**
 * 从此列表中删除所有元素。 此调用返回后,列表将为空
 */
public void clear() {
    modCount++;
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    size = 0;
}

/**
 * 按照指定集合的迭代器返回的顺序,将指定集合中的所有元素附加到此列表的末尾
 * 如果在操作正在进行时修改了指定的集合,则此操作的行为是未定义的
 *(这意味着如果指定的集合是这个列表,并且这个列表是非空的,那么这个调用的行为是未定义的。)
 *
 * @param c 包含要添加到此列表的元素的集合
 * @return true如果此列表因调用而更改
 * @throws NullPointerException 如果指定的集合为空
 */
public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew); 
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

/**
 * 将指定集合中的所有元素插入此列表,从指定位置开始
 * 将当前位于该位置的元素(如果有)和任何后续元素向右移动(增加它们的索引)
 * 新元素将按照指定集合的迭代器返回的顺序出现在列表中。
 *
 * @param index 插入指定集合中第一个元素的索引
 * @param c 包含要添加到此列表的元素的集合
 * @return <tt>true</tt> 如果此列表因调用而更改
 * @throws IndexOutOfBoundsException {@inheritDoc}
 * @throws NullPointerException 如果指定的集合为空
 */
public boolean addAll(int index, Collection<? extends E> c) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);
    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

copyOf(在JDK中)

/**
 * 复制指定的数组,用空值截断或填充(如有必要),使副本具有指定的长度
 * 对于在原始数组和副本中都有效的所有索引,这两个数组将包含相同的值
 * 对于在副本中有效但在原始数组中无效的任何索引,副本将包含null
 * 当且仅当指定长度大于原始数组的长度时,此类索引才会存在
 * 生成的数组 属于newType类。
 *
 * @param <U> 原始数组中对象的类
 * @param <T> 返回数组中对象的类
 * @param original 要复制的数组
 * @param newLength 要返回的副本的长度
 * @param newType 要返回的副本的类别
 * @return 原始数组的副本,被截断或用空值填充以获得指定的长度
 * @throws NegativeArraySizeException 如果newLength为负
 * @throws NullPointerException 如果original为空
 * @throws ArrayStoreException 如果从original复制的元素不是可以存储在类newType的数组中的运行时类型
 */
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不会写代码的猴子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值