ArrayList和LinkedList的区别

本文详细分析了ArrayList和LinkedList两种数据结构的特点。ArrayList基于动态数组,适合随机访问但插入和删除较慢,特别是中间插入。LinkedList适合插入和删除但不适合查询,因遍历效率低。文中还探讨了初始化ArrayList时指定容量的重要性以及LinkedList遍历的优化建议。
摘要由CSDN通过智能技术生成

ArrayList:

特点:

      基于动态数组,连续内存存储,数组通过角标进行随机访问的时间复杂度为O(1),CPU的内部缓存结构会缓存连续的内存片段,可以大幅降低读取内存的性能开销。因为数组中存放到数据类型一样,所以数据占据的大小相同(偏移量)

       扩容机制:因为数组长度固定,超出长度存数据时需要新建数组,每次扩容为原先数组长度的1.5倍,然后将老数组的数据拷贝到新数组,如果不是尾部插入数据还会涉及到元素的移动(往后复制一份,插入新元素),使用尾插法并指定初始容量可以极大提升性能、甚至超过linkedList (需要创建大量的node对象)

问题:ArrayList是有序的,插入数据的顺序和遍历是数据是一致的,为什么还说在数组内部插入数据慢呢?

因为:当指定位置插入时,后面的数据会逐个往后移动

    public static void main(String[] args) {

        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.add("hello1");
        arrayList.add("hello2");
        arrayList.add("hello3");
        arrayList.add(3,"hello4");
        arrayList.add(3,"hello5");
        arrayList.add(3,"caibi");

        Iterator<String> ite=arrayList.iterator();
        while(ite.hasNext())//判断下一个元素之后有值
        {
            System.out.println(ite.next());
        }

    }

/*
结果:
    hello1
    hello2
    hello3
    caibi
    hello5
    hello4

ArrayList插入默认为尾插法,慢的原因还有在数组中间删除一个元素时,后面的元素会逐个前移,而且进行扩容的时候也很慢,这才是是ArrayList在更新慢的原因。

 

当创建ArrayList数组时,可以指定初始数组长度:

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

未指定是默认如下:

JDK1.7时默认动态数组的长度是10

JDK1.8时默认动态数组的长度为0,当执行第一次添加操作时扩容为10(懒加载);

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

 对于DEFAULTCAPACITY_EMPTY_ELEMENTDATA的源码解释:


    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};


LinkedList:

特点:

基于链表,可以存储在分散的内存中,适合做数据插入及删除操作,插入的时间复杂度几乎为O(1),不适合查询:需要逐一遍历遍历LinkedList必须使用iterator不能使用for循环,因为每次for循环体内通过get()取得某一 元素时都需要对list重新进行遍历,性能消耗极大。另外不要试图使用indexOf等返回元素索引,并利用其进行遍历,使用indexIOf对list进行 了遍历,当结果为空时会遍历整个列表。

indexOf源码如下:

    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }

为什么不让使用for循环遍历LinkedList呢

举例:


public static void main(String[] args) {
	List<Integer> list = new LinkedList<Integer>();
	for (int i = 0; i < 10; i++) {
		Random r = new Random();
		Integer tmp = r.nextInt();
		list.add(tmp);
	}
	for (int i = 0; i < list.size(); i++) {
		System.out.println(list.get(i));
	}
	for (Integer i : list) {
		System.out.println(i);
	}
	Iterator<Integer> iter = list.iterator();
	while (iter.hasNext()) {
		System.out.println(iter.next());
	}
}

因为for需要内get方法的问题:

    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }

get方法调用了node方法

node方法每次都会进行一次全局遍历,性能非常低:

    Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

 原因就在两个for循环里面,以前者为例:
1、get(0),直接拿到0位的Node0的地址,拿到Node0里面的数据

2、get(1),直接拿到0位的Node0的地址,从0位的Node0中找到下一个1位的Node1的地址,找到Node1,拿到Node1里面的数据

3、get(2),直接拿到0位的Node0的地址,从0位的Node0中找到下一个1位的Node1的地址,找到Node1,从1位的Node1中找到下一个2位的Node2的地址,找到Node2,拿到Node2里面的数据。

......

所以尽量不要用

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值