较真,ArrayList和LinkList增加,插入速度比较,代码详解

网上有很多ArrayList和LinkList的各种对比,以前学习这两个集合的时候老师讲的是:查询ArrayList快,新增和删除LinkLink快。需要频繁查询用ArrayList,需要频繁增删用LinkLink。
但是网上百度的话说法又不一样。
下面就上代码测试吧:(测试是使用的JDK1.8其他版本结论可能不同,不做版本更变测试)

//先测试新增速度,使用add()方法 先插入10000条数据比较
//比较速度的时候为了不影响 要单独跑 不能两个列表同时插入
public static void main(String[] args){
        List<String> array = new ArrayList<String>();
        LinkedList<String> link = new LinkedList<String>();
        //array 插入
        long startlist = System.currentTimeMillis();
        for(int i = 0; i < 10000; i ++){
            array.add("123"+i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("array="+(endTime-startlist));
       
       //link插入
        /** 
        long startlist2 = System.currentTimeMillis();
        for(int i = 0; i < 10000; i ++){
            link.add("123"+i);
        }
        long endTime2 = System.currentTimeMillis();
        System.out.println("link="+(endTime2-startlist2));
        */
    }
    //分别运行3次
    //array=10 array=9 array=10
    // link=10 link=10 link=10

可以得出结论 在添加10000条数据的时候 两个数组几乎没差别。
继续测试,还是刚才代码,把for循环里面参数改为1000000,

一样 分别运行3次得出结果
array=577 array=518 array=537
link=778 link=752 link=691 
得出结果 arrayList在新增百万数据时要快一点,但是差别也不大

再加大数据(我IDEA跑的 再加到1000W循环是保存内存溢出 加大了JVM内存空间再战!!!):

一样 分别运行3次得出结果
link=6628 link=6873 link=6469
array=5934 array=5725 array=5767

实际证明 千万数据新增还是ArrayList要快一点。

是不是感觉已经推翻了以前学到的东西,不急,下面来分别研究两个集合在新增的时候到底做了什么操作,上源码:

在这里插入代码片

结论 在不修改指针的时候,就算ArrayList发生了扩容copy操作,在往数据末尾新增的时候速度还是比LinkList速度快。

//ArrayList源码:
public boolean add(E e) {
		//增加数组大小(指针移动) 如果需要就扩容
        ensureCapacityInternal(size + 1); 
        //元素赋值
        elementData[size++] = e;
        return true;
    }
    //ensureCapacityInternal方法 判断是否小于数组初始值10
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }
    //当前数组+1 如果大于当前数组长度就进行扩容
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    //这个就是扩容的最本质方法了 新建一个1.5倍长度的数组,在把老数组拷贝过来。copyOf方法使用的是 native void arraycopy 效率很快
     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 is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

下面是LinkList源码分析:

//linkList的add方法看起来就简单了很多
 public boolean add(E e) {
        linkLast(e);
        return true;
    }
//找到最好一个参数 创建一个新的Node对象直接新增,修改前一个Node对象,看上去很简单,但是实际上新增了一个对象,修改了一个对象。
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

继续测试,测试插入方法,一样的测试代码

public static void main(String[] args){
        ArrayList<String> array = new ArrayList<String>();
        LinkedList<String> link = new LinkedList<String>();
        //先创建数据
        for(int i = 0; i < 10000; i ++){
            link.add("123"+i);
        }
        //插入新数据
        long startlist2 = System.currentTimeMillis();
        for(int i = 0; i < 100000; i ++){
            link.add(0,"123"+i);
        }
        long endTime2 = System.currentTimeMillis();
        System.out.println("link="+(endTime2-startlist2));
	//还是分别执行 互不影响
      /*  for(int i = 0; i < 10000; i ++){
            array.add("123"+i);
        }
        long startlist = System.currentTimeMillis();
        for(int i = 0; i < 100000; i ++){
            array.add(0,"123"+i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("array="+(endTime-startlist));*/
    }
    //分别执行3次 看结果
    //link=63 link=58 link=65
    //array=3469 array=3398 array=3244

插入速度通过测试得出,linkList直接完爆ArrayList,
这又是为什么呢???
上源码:

//ArrayList 插入方法 
public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //这里可以看出 插入的时候 是直接调用的arraycopy copy了一个新的数组
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

ArrayList很粗暴的每次插入都在复制新的数组,

//linkList 先判断是不是最后一个,最好一个调用和刚才新增一样的方法创建对象,不是最后一个
public void add(int index, E element) {
        checkPositionIndex(index);

        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
//不是最后一个 使用插入方法,新增对象,修改所以对象指针。
void linkBefore(E e, Node<E> succ) {
        // assert succ != null;
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)
            first = newNode;
        else
            pred.next = newNode;
        size++;
        modCount++;
    }

可以得出最终结论,在新增对象的时候两个集合ArrayList要快一点,但是基本没什么区别。
但是在插入操作的时候,LinkList效率完爆ArrayList。

最后在补充一下两个数组删除remove方法的效率差距:

//这是ArrayList的remove源码 这里可以看出 删除方法也是调用arraycopy
public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

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

        return oldValue;
    }


//而LinkList依旧只是在对节点对象进行操作
  public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;

        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;
        size--;
        modCount++;
        return element;
    }

由此可见,LinkList在删除的效率上依旧完爆ArrayList。
注:以上源码只针对JDK1.8!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值