Java Collections - ArrayList

ArrayList

ArrayList内部使用动态数组的形式来保存数据,每次添加元素的时候,会插入到数组尾部。数组大小不够时,会扩容。
与ArrayList相关的还有一个LinkedList,内部用链表来存储数据。

初始化

初始化一个ArrayList的方法如下:

    ArrayList<Integer> numbers = new ArrayList<>();

如果不指定大小,默认的数组大小是10。

这个类里面定义了几个成员变量

    /**
    * Default initial capacity.
    * 数组的默认大小
    */
    private static final int DEFAULT_CAPACITY = 10;
  /**
     * Shared empty array instance used for empty instances.
     * 数组里没有元素时的空数组
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
     * DEFAULT_CAPACITY when the first element is added.
     * 存储数据的地方,如果调用无参构造函数,开始时指向EMPTY_ELEMENTDATA,添加元素,大小就会扩展到10;如果调用有参构造参数,就会指向一个初始化大小的数据。
     */
    private transient Object[] elementData;

    /**
     * The size of the ArrayList (the number of elements it contains).
     * 数组里真实存储的元素个数
     * @serial
     */
    private int size;

    //记录了ArrayList被修改的次数,在高并发的时候,子类可以通过这个参数来判断普通数组时,是不是被其他线程修改了,抛出ConcurrentModificationException。没有深入研究
    protected transient int modCount = 0;

添加元素

用add方法往里面添加元素,会加在数据的最尾部。

    numbers.add(10);
    numbers.add(100);
    numbers.add(150);

在添加元素之前,会先判断数据的空间是否充足。

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

ensureCapacityInternal方法会保证数据的大小。

private void ensureCapacityInternal(int minCapacity) {
//EMPTY_ELEMENTDATA是一个空数组,当ArrayList里没有元素时,elementData指向空数组,这个可以不占用空间。
//取默认大小和minCapacity中较大的那个,默认大小是10
   if (elementData == EMPTY_ELEMENTDATA) {
       minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
   }
        //保证数组的大小
   ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
   modCount++;

   // overflow-conscious code
   //如果当前elementData的大小比需要的最小容量还小,就要调用grow方法来扩充容量。
   if (minCapacity - elementData.length > 0)
       grow(minCapacity);
}

grow方法用来扩容,扩大到原始大小的2.5倍。使用了位移的方法来加快运算速度。

private void grow(int minCapacity) {
   // overflow-conscious code
   int oldCapacity = elementData.length;
   //原始容量的2.5倍
   int newCapacity = oldCapacity + (oldCapacity >> 1);
   if (newCapacity - minCapacity < 0)
       newCapacity = minCapacity;   //取最小容量
   //如果超过了最大容量(Integer.MAX_VALUE - 8),就使用Integer.MAX_VALUE
   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的时候,最好估算好大小,分配合适的空间。否则在扩容的时候会频繁的拷贝数据,影响性能。

遍历元素

遍历的方法有两种。使用index遍历和for each遍历。

    System.out.println("#1: indexed loop for iteration");
    for (int i = 0; i < numbers.size(); i++) {
      System.out.println(numbers.get(i));
    }

    System.out.println("2#: iteration");
    for (Integer value : numbers) {
      System.out.println(value);
    }

它们的性能如何呢?对于ArrayList,index遍历的性能要更优,但是也优不了多少。
在Java里,foreach语法是iterator的变形

for(int i : weights) {
    sum += i;
}

等同与:

for(Iterator<Integer> i = weights.iterator(); i.hasNext()) {
    sum += i.next();
}

删除元素

在ArrayList删除元素的耗时与位置有关。
删除最后一个元素:直接把数组的大小减1就行。
删除中间某个元素:需要将它后面的部分都拷贝一次,开销比较大。
因此尽量不要在ArrayList上频繁删除元素。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值