`
inspires
  • 浏览: 15945 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

ArrayList 与 LinkedList 比较(实现方式)

    博客分类:
  • Java
阅读更多

 

        欢迎访问我的个人博客

         面试的时候,经常会有面试官提问Collection接口下面实现类的区别,以及各自适用场景,不少人都能答出来:ArrayList基于数组实现,善于随机访问和遍历集合,做随机位置插入和删除性能较差,而ArrayList基于链表结构实现(双向链表),在做随机位置插入时性能较好于ArrayList,在做遍历和随机访问时性能较差。但很多人不知道到底为什么基于数组实现随机访问就快?任意位置插入性能就差?而基于链表实现的正好相反呢?

         要弄清楚这个问题,必须要清楚其实现方式,最好的办法就是去看源码,这是一种好的

学习习惯。

从上面图可以看到List接口下的所有实现类和父接口,其中当然包括本文介绍的主角儿ArrayList,LinkedList,既然是实现自同一接口,那么他们的规范自然是一样的。

         如上图,因为源码注释太多,看着烦躁,这里我用Ctrl+o可以看到List接口下面的所有方法,接下来,我们来看看他们各自的实现的代码吧!

         首先我们看ArrayList,在类中有个Object[] elementData,可以想到他是用来存放Arraylist中的数据的。

那么,它是怎样对数据进行增删改查操作的?

1
2
3
4
5
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
}

 

这是add方法,主要是将e赋值给数组,将数组长度加一,ensureCapacityInternal里面主要是做一些逻辑判断,请看grow方法

1
2
3
4
5
6
7
8
9
10
11
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);
    }

 

 

Arrays.copyOf(elementData, newCapacity);这行代码非常关键,他是影响ArrayList插入性能的主要因素,当数组下表超过数组长度时会调用这个方法,调用Arrays.copyOf的方法时会重新copy构建一个新的数组,会消耗大量的资源,导致性能变的很差。

下面看看随机插入add(int index, E element)

1
2
3
4
5
6
7
8
9
public void add(int index, E element) {
        rangeCheckForAdd(index);
  
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
}

 

此处更是不消多说,就是copy copy copy,大量的数组移动,导致性能底下。remove方法与add方法实现类似.

再来看看随机访问和遍历,get方法非常简单

1
2
3
4
public E get(int index) {
        rangeCheck(index);
        return elementData(index);
}

 

直接指向数组下标位置的元素返回,当然是最快的,遍历亦是如此。

再来看看LinkedList的内部实现吧

 

内部有两个Node对象,一个指向集合第一个,一个指向最后一个,Node内部代码如下

1
2
3
4
5
6
7
8
9
10
11
private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
  
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
}

 

每一个Node都有nextprev两个对象,相互关联这就形成了一条链子,所以这个叫做链表结构(双向),用网上的一个图片。

 

再来看看他们实现addremove的方法吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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++;
}

 

从上面代码不难发现,普通的add会调用linkLast,linkdLast方法内部会新建一个Node对象,将要存储的值放入node中,同时把当前的node绑定到集合最末端,时间复杂度为O(n).

再看指定位置的插入,add(int index, E element)方法。

1
2
3
4
5
6
7
8
public void add(int index, E element) {
        checkPositionIndex(index);
  
        if (index == size)
            linkLast(element);
        else
            linkBefore(element, node(index));
}

 

当需要插入的位置为集合大小时,可以直接插入集合尾部,否则,则需要找到指定位置的下标然后修改他们nextprev的指向,这里会有一个遍历,JDK对其做了优化,不同位置从两端遍历,这样让遍历的查找时间降到最低。代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
        }
 }

 

removeadd逻辑类似。

 

总结一下,ArrayList得其天赋是数组,数组在随机访问时的速度是与生俱来的,而其软肋就是数组是固定的,大小固定,位置固定,所以你做新增(特别是中间段的新增或者是长度已达目前初始大小时),删除会频繁移动数组,导致性能底下。

而在LinkedList里面,由于他使用双向链表结构实现,那么新增和删除,只需要修改相应的指向就行了,在中间位置插入会比在末尾插入性能要差点,在随机访问时,由于需要遍历,所以速度逊于ArrayList,在做遍历的时候也是一样,使用迭代器可以缓解这个问题。

         值得一提的时,假设我需要一个长度为固定数量S的集合,默认向里面add数据,给ArrayList赋予初始初始容量S,那么ArrayList插入的性能会优于LinkedList,请看测试代码和运行结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void main(String[] args) {
        int size=10000000;
        testArraylistAdd(size);
        testLikedlistAdd(size);
    }
  
    static void testArraylistAdd(int size) {
        long t1 = System.currentTimeMillis();
        List<Integer> list = new ArrayList<Integer>(size);
        for (int i = 0; i < size; i++) {
            list.add(i);
        }
        long t2 = System.currentTimeMillis();
        System.out.println("测试" + size + "条数据,插入ArrayList所花费时间 为:" + (t2 - t1));
    }
  
    static void testLikedlistAdd(int size) {
        long t1 = System.currentTimeMillis();
        List<Integer> list = new LinkedList<Integer>();
        for (int i = 0; i < size; i++) {
            list.add(i);
        }
        long t2 = System.currentTimeMillis();
        System.out.println("测试" + size + "条数据,插入LinkedList所花费时间 为:" + (t2 - t1));
    }

 

测试10000000条数据,插入ArrayList所花费时间 为:78

测试10000000条数据,插入LinkedList所花费时间 为:4738

主要是因为初始时指定了数组长度,插入时不需要频繁移动数组,所以速度才会优于LinkedList

1
1
分享到:
评论
2 楼 freezingsky 2014-08-18  
这种问题虽然也被问过不少次,但从来没有去看代码的实现,如果非要讲二者之间有何不同,其实数据结构这门课就已经说得够清楚了。
1 楼 hngmduyi 2014-08-18  
   

相关推荐

    ArrayList和Linkedlist1

    总的来说,理解ArrayList和LinkedList的基本特性和应用场景,以及如何处理与之相关的安全性问题,是Java程序员必备的知识。通过深入学习和实践,可以更好地利用这些数据结构提升程序效率和质量。

    ArrayList LinkedList Vector区别

    ArrayList、LinkedList、Vector 是 Java 中常用的数据结构实现类,它们都实现了 List 接口,但它们在存储方式、性能、线程安全性等方面有着不同特点。 首先,ArrayList 和 Vector 都是采用数组方式存储数据的,这种...

    Java中ArrayList和LinkedList区别 时间复杂度 与空间复杂度1

    在 Java 中,ArrayList 和 LinkedList 是两种常用的集合类,它们各自具有不同的特性和适用场景,主要体现在数据结构、访问效率和操作性能上。 1. 数据结构: - ArrayList 实现了一个动态数组,它内部是一个 Object...

    测试ArrayList和LinkedList的add方法

    测试ArrayList和LinkedList的add方法

    关于arraylist和linkedList的区别

    在Java编程语言中,`ArrayList`与`LinkedList`都是`List`接口的具体实现类,用于存储元素集合。虽然它们都实现了同样的接口并且提供了相同的基本功能,但在内部实现机制、性能特点以及适用场景等方面存在显著差异。 ...

    【Java面试题】ArrayList和LinkedList区别

    【Java面试题】ArrayList和LinkedList区别

    合理运用ArrayList与LinkedList

    在Java的集合框架中,ArrayList和LinkedList是两种常用的列表实现,它们都实现了List接口,但它们在内存管理和操作效率上存在显著差异。了解这些差异并根据具体应用场景选择合适的列表类型,能够有效提升J2EE应用...

    对ArrayList和LinkedList底层实现原理详解

    总结:ArrayList 和 LinkedList 是 Java 中两个常用的集合类,它们的底层实现方式不同,ArrayList 是基于数组实现的,而 LinkedList 是基于链表实现的。理解它们的底层实现方式可以帮助我们更好地使用它们。

    ArrayList LinkedList Vector性能对比

    在Java编程语言中,集合框架是处理数据的重要组成部分。ArrayList、LinkedList和Vector是三种常见的动态数组实现,...在深入源码阅读和实践过程中,我们可以更深入地理解这些类的设计思想和实现方式,提升编程技能。

    Arraylist与LinkedList区别

    2,随机访问的时候,ArrayList的效率比较高,因为LinkedList要移动指针,而ArrayList是基于 3,索引(index)的数据结构,可以直接映射到。 4,插入、删除数据时,LinkedList的效率比较高,因为ArrayList要移动数据。 ...

    比较ArrayList、LinkedList、Vector1

    - **实现原理**:Vector与ArrayList类似,也是基于动态数组,但它是线程安全的。 - **线程安全**:每个公共方法都在内部进行了同步,这意味着在多线程环境下无需额外的同步代码。 - **效率**:由于线程安全的实现...

    ArrayList LinkedList Vector性能测试

    在Java编程语言中,ArrayList、LinkedList和Vector是三种常见的动态数组实现,它们都在java.util包中,用于存储和管理对象的集合。这三个类都实现了List接口,提供了多种操作方法,但它们在内部实现和性能特性上有所...

    05丨ArrayList还是LinkedList?使用不当性能差千倍.html

    05丨ArrayList还是LinkedList?使用不当性能差千倍.html

    arraylist-linkedlist-test.zip

    ArrayList和LinkedList是Java集合框架中两种重要的动态数组实现,它们都是List接口的实现类,但它们在存储和操作数据方面有着显著的区别。本文件"arraylist-linkedlist-test.zip"主要探讨了在执行添加和删除元素操作...

    ArrayList Vector LinkedList 区别与用法.

    ### ArrayList、Vector、LinkedList 的区别与用法详解 在Java编程中,选择合适的数据结构对于程序的性能至关重要。本文将深入探讨ArrayList、Vector和LinkedList三种集合类的特点与使用场景,帮助开发者更好地理解...

    ArrayList-LinkedList-源码.rar

    《ArrayList与LinkedList源码解析》 在Java编程中,ArrayList和LinkedList是两种常见的动态数组,它们都是Java集合框架的一部分,提供了对元素存储和操作的功能。本篇将深入探讨ArrayList和LinkedList的内部实现...

    Java中ArrayList和LinkedList区别

    在Java编程语言中,ArrayList和LinkedList都是集合框架中两种重要的列表实现,它们分别基于不同的数据结构,具有不同的特性和性能特点。以下是对这两个类的详细分析: 1. 数据结构: - ArrayList是基于动态数组的...

    java 中ArrayList与LinkedList性能比较

    ArrayList与LinkedList性能比较在java中 ArrayList和LinkedList是java中两个常用的实现List接口的类,它们之间的性能比较是一个非常重要的知识点。 首先,让我们来了解ArrayList和LinkedList的实现原理。ArrayList...

    java中ArrayList与LinkedList对比详情

    在 Java 中,ArrayList 和 LinkedList 是两个常用的集合实现方式,它们都实现了 Collection 接口,但是它们之间存在着一些关键的差异。本文将通过实例对比 Java 中 ArrayList 和 LinkedList 的实现机制、性能差异、...

    当面试官问我ArrayList和LinkedList哪个更占空间时,我这么答让他眼前一亮

    ArrayList和LinkedList的比较与实现原理 在 Java 中,ArrayList 和 LinkedList 是两个常用的集合类,它们都是 List 接口的实现类,但它们之间有着鲜明的区别。今天,我们将深入探讨这两个集合类的实现原理和比较。 ...

Global site tag (gtag.js) - Google Analytics