java 学习之四 ArrayList

ArrayList的主要特点

ArrayList比较适合顺序添加、随机访问的场景
ArrayList允许元素数目为
ArrayList允许重复数据
       ArrayList是元素有序  (指的是插入和取出顺序相同)
        ArrayList是非线程安全

添加元素

有这么一段代码:

public static void main(String[] args)
{
    List<String> list = new ArrayList<String>();
    list.add("000");
    list.add("111");
}
看下底层会做什么,进入add方法的源码来看一下:
1 public boolean add(E e) {
2 ensureCapacity(size + 1);  // Increments modCount!!
3 elementData[size++] = e;
4 return true;
5 }
先不去管第2行的ensureCapacity方法,这个方法是扩容用的,底层实际上在调用add方法的时候只是给elementData的某个位置添加了一个数据而已,用一张图表示的话是这样的:


多说一句,我这么画图有一定的误导性。elementData中存储的应该是堆内存中元素的引用,而不是实际的元素,这么画给人一种感觉就是说elementData数组里面存放的就是实际的元素,这是不太严谨的。不过这么画主要是为了方便起见,只要知道这个问题就好了。

扩容

我们看一下,构造ArrayList的时候,默认的底层数组大小是10:  (有的地方看到的写的是16)

public ArrayList() {
this(10);
}
那么有一个问题来了,底层数组的大小不够了怎么办?答案就是扩容,这也就是为什么一直说ArrayList的底层是基于动态数组实现的原因,动态数组的意思就是指底层的数组大小并不是固定的,而是根据添加的元素大小进行一个判断,不够的话就动态扩容,扩容的代码就在ensureCapacity里面:
public void ensureCapacity(int minCapacity) {
   <span>	</span> modCount++;
<span>	</span>int oldCapacity = elementData.length;
<span>	</span>if (minCapacity > oldCapacity) {
  <span>		</span>Object oldData[] = elementData;
        <span>	</span>int newCapacity = (oldCapacity * 3)/2 + 1;
        <span>	</span>if (newCapacity < minCapacity)
    <span>		</span>newCapacity = minCapacity;
        <span>	</span>// minCapacity is usually close to size, so this is a win:
        <span>	</span>elementData = Arrays.copyOf(elementData, newCapacity);
<span>	</span>}
}

看到扩容的时候把元素组大小先乘以3,再除以2,最后加1。可能有些人要问为什么?我们可以想:

1、如果一次性扩容扩得太大,必然造成内存空间的浪费

2、如果一次性扩容扩得不够,那么下一次扩容的操作必然比较快地会到来,这会降低程序运行效率,要知道扩容还是比价耗费性能的一个操作

所以扩容扩多少,是JDK开发人员在时间、空间上做的一个权衡,提供出来的一个比较合理的数值。最后调用到的是Arrays的copyOf方法,将元素组里面的内容复制到新的数组里面去:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
       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;
}

用一张图来表示就是这样的:

删除元素

接着我们看一下删除的操作。ArrayList支持两种删除方式:

1、按照下标删除

2、按照元素删除,这会删除ArrayList中与指定要删除的元素匹配的第一个元素

对于ArrayList来说,这两种删除的方法差不多,都是调用的下面一段代码:

int numMoved = size - index - 1;
if (numMoved > 0)
    System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
elementData[--size] = null; // Let gc do its work

其实做的事情就是两件:

1、把指定元素后面位置的所有元素,利用System.arraycopy方法整体向前移动一个位置

2、最后一个位置的元素指定为null,这样让gc可以去回收它

比方说有这么一段代码:

public static void main(String[] args)
{
    List<String> list = new ArrayList<String>();
    list.add("111");
    list.add("222");
    list.add("333");
    list.add("444");
    list.add("555");
    list.add("666");
    list.add("777");
    list.add("888");
    list.remove("333");
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章