java趣谈(3)ArrayList与LinkedList插入效率探究

昨天做题时,看到了Collections的reverse方法,区分了arraylist和linkedlist的情况,一时兴起,看了看源码,发现了一些有趣的情况。

情况一 尾插

public static void main(String[] args){
		List list1 =new ArrayList();
		long t1 = System.currentTimeMillis();
		for (int i = 0; i < 100000; i++) {
			list1.add(i);
		}
		long t2 = System.currentTimeMillis();
		System.out.println(t2-t1);
        List list2 =new LinkedList();
        long t3 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
			list2.add(i);
		}
        long t4 = System.currentTimeMillis();
        System.out.println(t4-t3);
	}

结果 



情况二  中部插入

public static void main(String[] args){
		List list1 =new ArrayList();
		long t1 = System.currentTimeMillis();
		for (int i = 0; i < 100000; i++) {
			list1.add(i/2,i);
		}
		long t2 = System.currentTimeMillis();
		System.out.println(t2-t1);
        List list2 =new LinkedList();
        long t3 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
			list2.add(i/2,i);
		}
        long t4 = System.currentTimeMillis();
        System.out.println(t4-t3);
	}


情况三 头插

public static void main(String[] args){
		List list1 =new ArrayList();
		long t1 = System.currentTimeMillis();
		for (int i = 0; i < 100000; i++) {
			list1.add(0,i);
		}
		long t2 = System.currentTimeMillis();
		System.out.println(t2-t1);
        List list2 =new LinkedList();
        long t3 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
			list2.add(0,i);
		}
        long t4 = System.currentTimeMillis();
        System.out.println(t4-t3);
	}


要解释上述情况,就要从这arraylist和linkedlist的add方法说起了

先看arraylist的

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
public void add(int index, E element) {
        rangeCheckForAdd(index);  //检查是否有异常

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);  //复制数组,复制长度为size-index
        elementData[index] = element;
        size++;
    }

所以,在进行尾插的时候,执行的是第一个add方法,数组扩容,在尾部插入即可,只需6ms

中部插入,每插入一个元素,要复制一半的数组内容,所以是320ms

头插,每插入一个元素,要复制整个数组的内容,所以需要726ms(这几个时间只是大致值,每次输出并不完全相同)


再看linkedlist的

public boolean add(E e) {
        linkLast(e);
        return true;
    }
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 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++;
    }

从代码中可以看出,头插(prev==null)和尾插(index==size)时,都是在链表端部直接插入,无需遍历,所以用时很少,

而中部插入时,每次都要遍历一半的链表,才能进行插入,自然耗时很多了


但是又引发了一个新的问题,到底谁的插入效率更高呢?

直接上结果







在开始时,两者相差不多,十万条数据时,linkedlist显示出优势,但百万条数据时,arraylist比linkedlist用时少了很多,千万数据时,linkedlist又反超了,结果很是奇异~

翻了下arraylist的扩容原理,默认10,每次扩容是之前容量的1.5倍

而linkedlist只是添加元素

查了一下 有一种说法:linkedlist是每一次都需要去new新对象,修改与链表之间的相互引用。一次性分配内存总是会比多次分配内存花费的时间少,可以解释arraylist第一次“超车”,但无法解释千万数据级的情况,唔,这个问题只能先搁置于此了


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值