從Arraylist的add(int index, E object)方法展開源碼分析以及與Linkedlist的存取數據耗時對比

  • 1 先比較下Arraylist和Linkedlist的存取數據的速度

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_content_detail);
      ButterKnife.bind(this);
    
      System.out.println("ArrayList添加" + N + "條耗時:" + addElements(new ArrayList()));
      System.out.println("LinkedList添加" + N + "條耗時:" + addElements(new LinkedList()));
    
      List list1 = addList(new ArrayList<>());
      List list2 = addList(new LinkedList<>());
      System.out.println("ArrayList查找" + N + "條耗時:" + readList(list1));
      System.out.println("LinkedList查找" + N + "條耗時:" + readList(list2));
    
    }
    

    private long addElements(List list) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
    list.add(0, "" + i);
    }
    return System.currentTimeMillis() - start;
    }

    private long readList(List list) {
    long start = System.currentTimeMillis();
    for (int i = 0, j = list.size(); i < j; i++) {
    list.get(i);
    }
    return System.currentTimeMillis() - start;
    }

    private List addList(List list) {
    for (int i = 0; i < N; i++) {
    list.add(0, "" + i);
    }
    return list;
    }

輸出結果:

03-28 15:09:30.321 14873-14873/com.amazingokc.zhihucolumn I/System.out: ArrayList添加50000條耗時:2249
03-28 15:09:30.551 14873-14873/com.amazingokc.zhihucolumn I/System.out: LinkedList添加50000條耗時:233
03-28 15:09:32.911 14873-14873/com.amazingokc.zhihucolumn I/System.out: ArrayList查找50000條耗時:5
03-28 15:09:45.451 14873-14873/com.amazingokc.zhihucolumn I/System.out: LinkedList查找50000條耗時:12540

可以看到各有優缺點

其實從Linkedlist的源碼可以知道,對于統一個鏈表而言,從指定位置添加數據與獲取數據消耗時間是幾乎相同的,將上面代碼稍微改改

private long addElements(List list) {

    for (int i = 0; i < 40000; i++) {
        list.add(0, "" + i);
    }
    long start = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
        list.add(20000, "" + i);
    }
    return System.currentTimeMillis() - start;
}

private long readList(List list) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 10000; i++) {
        list.get(20000);
    }
    return System.currentTimeMillis() - start;
}

輸出:

03-28 16:24:37.178 23163-23714/com.amazingokc.zhihucolumn I/System.out: ArrayList添加10000條耗時:410
03-28 16:24:41.658 23163-23714/com.amazingokc.zhihucolumn I/System.out: LinkedList添加10000條耗時:4321
03-28 16:24:43.928 23163-23714/com.amazingokc.zhihucolumn I/System.out: ArrayList查找10000條耗時:1
03-28 16:24:48.228 23163-23714/com.amazingokc.zhihucolumn I/System.out: LinkedList查找10000條耗時:4298

之前耗時相差之所以這么大,是因為開始的時候鏈表是慢慢變長的,讀取的時候鏈表一直是最大值,所以遍歷鏈表的時候總的時間就不一樣了。

  • 1 Arraylist的add(int index, E object)方法

Arraylist的add(int index, E object)方法之所以耗時比較大,是由數組的結構所決定的,數組是連續存儲的,在操作數組中的數據時就可以根據首地址的偏移量直接存取相應位置上的數據,但是如果要在數據組中任意位置上插入一個元素,就需要先把后面的元素集體向后移一位為其空出存儲空間。與之相反,鏈表是離散存儲的,所以在插入一個數據時只要申請一片新空間,然后將其中的連接關系做一個修改就可以,但是顯然在鏈表上查找一個數據時就要逐個遍歷了??聪略创a實現:

 /**
 *Inserts the specified object into this {@code ArrayList} at the specified
 * location. The object is inserted before any previous element at the
 * specified location. If the location is equal to the size of this
 * {@code ArrayList}, the object is added at the end.
 *
 * @param index
 *            the index at which to insert the object.
 * @param object
 *            the object to add.
 * @throws IndexOutOfBoundsException
 *             when {@code location < 0 || location > size()}
 */
@Override public void add(int index, E object) {
    Object[] a = array;
    int s = size;
    if (index > s || index < 0) {
        throwIndexOutOfBoundsException(index, s);
    }

    if (s < a.length) {
        System.arraycopy(a, index, a, index + 1, s - index);
    } else {
        // assert s == a.length;
        Object[] newArray = new Object[newCapacity(s)];
        System.arraycopy(a, 0, newArray, 0, index);
        System.arraycopy(a, index, newArray, index + 1, s - index);
        array = a = newArray;
    }
    a[index] = object;
    size = s + 1;
    modCount++;
}

一般情況下應該進入到else里面,第一個arraycopy會將原來的數組a從0開始的位置復制index個元素放到新建數組newArray的0位置排放,第二個arraycopy會將原來的數組a的其他元素放到新建數組newArray的index + 1位置排放,此時newArray的index位置是沒有放元素的,這個位置就是我們需要添加元素的位置,a[index] = object;完成了添加操作。所以每次添加一個元素的時候,都會對原來數組的元素進行復制工作,add(E object)方法是在尾部添加元素,只需要將全部元素放在新數組的前面,然后在新數字尾部添加一個元素即可,速度應該會相對快一些。刪除remove(int index)的源碼原理基本相似,這就是Arraylist的增刪操作比較耗時的原因。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容