JAVA容器之List

List的实现类主要是ArrayList和LinkedList,两个主要的差异是ArrayList是经过数组实现的,但LinkedList是经过链表实现。
能够想象,ArrayList在随机访问效率上远高于LinkedList,由于LinkedList访问一个元素必须从头节点开始依次访问知道找到目标节点,因此时间复杂度为O(n),而ArrayList的随机访问复杂度几乎是O(1)。
但频繁进行插入删除数据的时候,LinkedList的效率远高于ArrayList,由于LinedList只须要改变指针(引用)的内容,时间复杂度为O(1),而ArrayList添加与删除须要大量移动元素,时间复杂度为O(n)。java

遍历效率分析

public class ListTest {
    List<Integer> array = new ArrayList<Integer>();
    List<Integer> link = new LinkedList<Integer>();
    private final int size;
    private long start, end;

    public ListTest(int size) {
        this.size = size;
        for (int i = 0; i < size; i++) {
            int t = (int) (Math.random() * 1000);
            array.add(t);
            link.add(t);
        }
    }

    public void ArrayIteratorTraversal() {
        Iterator<Integer> i = array.iterator();
        int t;
        start = System.currentTimeMillis();
        while (i.hasNext()) {
            t = i.next();
        }
        end = System.currentTimeMillis();
        System.out.println("ArrayList使用Iterator遍历" + size + "个数据花了"
                + (end - start) + "ms");
    }

    public void LinkIteratorTraversal() {
        Iterator<Integer> i = link.iterator();
        int t;
        start = System.currentTimeMillis();
        while (i.hasNext()) {
            t = i.next();
        }
        end = System.currentTimeMillis();
        System.out.println("LinkedList使用Iterator遍历" + size + "个数据花了"
                + (end - start) + "ms");
    }

    public void ArrayListIteratorTraversal() {
        ListIterator<Integer> i = array.listIterator();
        int t;
        start = System.currentTimeMillis();
        while (i.hasNext()) {
            t = i.next();
        }
        end = System.currentTimeMillis();
        System.out.println("ArrayList使用ListIterator遍历" + size + "个数据花了"
                + (end - start) + "ms");
    }

    public void LinkListIteratorTraversal() {
        ListIterator<Integer> i = link.listIterator();
        int t;
        start = System.currentTimeMillis();
        while (i.hasNext()) {
            t = i.next();
        }
        end = System.currentTimeMillis();
        System.out.println("LinkedList使用ListIterator遍历" + size + "个数据花了"
                + (end - start) + "ms");
    }

    public void ArrayCycleTraversal() {
        int t;
        start = System.currentTimeMillis();
        for (int i = 0; i < size; i++)
            t = array.get(i);
        end = System.currentTimeMillis();
        System.out.println("ArrayList使用循环遍历" + size + "个数据花了"
                + (end - start) + "ms");
    }

    public void LinkCycleTraversal() {
        int t;
        start = System.currentTimeMillis();
        for (int i = 0; i < size; i++)
            t = link.get(i);
        end = System.currentTimeMillis();
        System.out.println("LinkedList使用循环遍历" + size + "个数据花了"
                + (end - start) + "ms");
    }

    public static void main(String[] args) {
        ListTest t = new ListTest(10000000);
        System.out.println("*******开始*******");
        t.ArrayIteratorTraversal();
        t.LinkIteratorTraversal();
        t.ArrayListIteratorTraversal();
        t.LinkListIteratorTraversal();
        t.ArrayCycleTraversal();
// t.LinkCycleTraversal();
        System.out.println("*******结束*******");
    }
}

其中分别用了Iterator,ListIterator和循环来访问ArrayList和LinkedList,输出结果为
//输出结果
*开始*
ArrayList使用Iterator遍历100000个数据花了9ms
LinkedList使用Iterator遍历100000个数据花了9ms
ArrayList使用ListIterator遍历100000个数据花了8ms
LinkedList使用ListIterator遍历100000个数据花了9ms
ArrayList使用循环遍历100000个数据花了6ms
LinkedList使用循环遍历100000个数据花了8049ms
*结束*
//输出结果数组

使用迭代器时,ArrayList比LinkedList要快上很多,屡次测试大约是三倍的样子,Iterator和ListIterator遍历一样的容器效率差很少。可是经过循环遍历,LinkedList跟ArrayList差了三个数量级,其实用循环遍历,看似是顺序遍历List,可是,经过List.get()的方式过的元素值,实际上是经过随机访问来实现的,因此也从这个例子能够证实上面所说LinkedList的随机访问效率是很是差的。
因为数据再增加,最后一项LinkedList循环遍历的测试等待时间很是长,因此把这一项测试去掉,你们知道这个效率很是差就行了。咱们把测试数据规模扩大到10billion:
//输出结果
*开始*
ArrayList使用Iterator遍历10000000个数据花了37ms
LinkedList使用Iterator遍历10000000个数据花了121ms
ArrayList使用ListIterator遍历10000000个数据花了38ms
LinkedList使用ListIterator遍历10000000个数据花了117ms
ArrayList使用循环遍历10000000个数据花了30ms
*结束*
//输出结果
扩大数据规模后发现有一点是跟上一次测试结果相同的,就是ArrayList的效率明显比LinkedList要高一些,并且另外发现一个问题,用循环遍历ArrayList比用迭代器也要快一些。这是为何呢?
一、 为何ArrayList循环遍历比迭代器遍历快?markdown

public E next() {
checkForComodification();
     int i = cursor;
     if (i >= size)
        throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
}

这是ArrayList中一个继承了Iterator的内部类的next()方法,看了源代码就能够发现其实ArrayList的Iterator就是直接访问ArryList中用于存放数据的一个数组,可是多了一些检查工做。并且,除此以外,遍历时每次iterator.hasNext()也须要额外的时间开销,因此这样遍历天然会比较满一些。
二、 为何用迭代器遍历ArrayList比LinkedList快一些呢?
这个问题从源码角度还真很差说,搜了一些资料,发现你们对这个效率缘由答案都不是特别明确,依稀看到有人的解释我以为仍是蛮有道理的,就是ArrayList的数据结构内存分配是连续的,而LinkedList是不连续的,访问下一个元素的时候须要经过地址去查找,因此有了额外的时间开销。数据结构

操做Arrays.asList()

有这么一使用Arrays.asList的例子:dom

List<Integer>list=Arrays.asList(5,6,8,2,45,1,2,8,5,125,6,8,5);
        list.add(5);

经过Arrays.asList方法把一串数字转化成list,而后调用list中的add方法添加元素。可是运行会发现,编译器抛出了一个java.lang.UnsupportedOperationException异常,问题就在add方法,其实除了add方法,remove,retain等对list元素个数会产生变化的方法都是会报这个异常的,究其缘由,看一下Arrays.asList的源码
乍一看,发现Arrays.asList返回的就是一个ArrayList对象,可是仔细看发现这个ArrayList并不是java.util.ArrayList,而是Arrays的一个内部类,并且没有实现刚才说的add那些方法,而是直接继承了AbstractList,而后AbstractList并无实现add等方法,只是直接抛出了UnsupportedOperationException异常,因此才会有上述代码发生的那些状况。测试

正确的使用:大数据

List<Integer> list = new ArrayList<Integer>(Arrays.asList(5, 6, 8, 2,
                45, 1, 2, 8, 5, 125, 6, 8, 5));
        list.add(5);

总结

对于LinekdList和ArrayList的差别,主要就差异在随机访问与修改,因此建议以下:
一、 若是须要大量删除与添加元素,使用LinkedList
二、 若是须要大量随机访问元素,使用ArrayList
三、 若是须要常常遍历,二者都可,ArrayList效率略高,可是若是选用LinedList,在遍历时尽可能使用迭代器,不然至关于随机存取,效率很是低。this

相关文章
相关标签/搜索