Java中ArrayList和LinkedList区别

目录
  • 1 前言
  • 2 数据结构的区别
    • 2.1 ArrayList
    • 2.2 LinkedList
    • 2.3 使用场景
  • 3 源码分析
    • 3.1 ArrayList核心源码
    • 3.2 LinkedList核心源码
  • 4 码农来洞见
    • 4.1为什么ArrayList比LinkedList要快
    • 4.2 注意ArrayList不同JDK版本源码
    • 4.3 高并发下如何保证集合数据的同步
    • 4.4 为什么Java的Vector类被认为是过时的或者废弃的

1 前言

许多语言,例如 Perl ,Python 和 Ruby ,都有集合的本地支持。有些语言(例如Python)甚至将基本集合组件(列表,映射和集合)作为内置函数包含在其中。

通常,程序总是根据运行时才知道的某些条件去创建新的对象。在此之前,无法知道所需对象的数量甚至确切类型。为了解决这个普遍的编程问题,需要在任意时刻和任意位置创建任意数量的对象。java.util 库提供了一套相当完整的集合类(collection classes)来解决这个问题,其中基本的类型有 List 、 Set 、 Queue 和 Map。这些类型也被称作容器类。

2 数据结构的区别

2.1 ArrayList

ArrayList是基于数组实现的,数组是典型的紧密结构,所以它使用索引在数组中搜索和读取数据快,可以直接返回数组中index位置的元素,因此在随机访问集合元素上有较好的性能。

2.2 LinkedList

LinkedList是基于双链表实现的,链表是典型的跳转结构,插入和删除只是指针指向的修改,所以它插入、删除中间元素快,因此在操作数据方面性能较好。

2.3 使用场景

如果应用程序对数据有较多的随机访问,ArrayList对象要优于LinkedList对象;

如果应用程序有更多的插入或者删除操作,较少的随机访问,LinkedList对象要优于ArrayList对象;

3 源码分析

下面我们通过JDK1.8源码源码分析一下核心功能。

3.1 ArrayList核心源码

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    
    //默认大小
    private static final int DEFAULT_CAPACITY = 10;
     
    //省略。。。
    
    //动态数组
    transient Object[] elementData; 
    
    //数组大小
    private int size;
    
    //空构造器
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    // 查询元素
    public E get(int index) {
        // 检查索引是否在范围内
        rangeCheck(index);                    
        return elementData(index);
    }
    
    //顺序添加元素
    public boolean add(E e) {
        //扩容
        ensureCapacityInternal(size + 1); 
        elementData[size++] = e;
        return true;
    }
    
    //从数组中间添加元素
    public void add(int index, E element) {
        // 检查索引是否在范围内
        rangeCheckForAdd(index);
        // 扩容
        ensureCapacityInternal(size + 1); 
        // 复制数组
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        // 替换元素
        elementData[index] = element;
        size++;
    }
    
    //是否要扩容
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    //确定容量
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    //计算数组容量
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    //动态数组扩容放法
    private void grow(int minCapacity) {
        // 
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 数组复制
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
     //省略。。。
    
  }

总结:

ArrayLis初始化构造器的时候数组为{},在调用add方法以后默认数组才赋值为新数组,新数组默认长度为10。
通过扩容机制判断原数组是否还有空间,若没有则重新实例化一个空间更大的新数组,把旧数组的数据拷贝到新数组中。
在执行查询操作时,先判断下标是否越界,然后在直接通过下标从数组中返回元素。

3.2 LinkedList核心源码

//JDK1.8
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{ 
    // 元素数量
    transient int size = 0;
    
    // 链表首节点
    transient Node<E> first;
    
    // 链表尾节点
    transient Node<E> last;
    
    // 空构造器
    public LinkedList() {
        
    }
    
    // Node内部类
    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;
        }
    }
    
    // 顺序添加元素
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    // 指定位置添加元素
    public void add(int index, E element) {
        // 检查是否越界
        checkPositionIndex(index);    
        // 在链表末尾添加,否则在之前插入元素
        if (index == size)            
            linkLast(element);
        else
            linkBefore(element, node(index));
    }
    
    // 添加元素e
    void linkLast(E e) {
        //把链表中last元素赋给l ,如果是第一个元素则为null
        final Node<E> l = last;
        //把元素封装为一个Node对象
        final Node<E> newNode = new Node<>(l, e, null);
        //把链表的last元素指向新的元素
        last = newNode;
        //如果添加的是第一个元素
        if (l == null){
            //把链表的first元素指向新的元素
            first = newNode;
        }
        //如果添加的不是第一个元素
        else{
            //把l的下一个元素指向新的元素
            l.next = newNode;            
        }
        //集合中元素数量加1
        size++;
        modCount++;
    }
    
    void linkBefore(E e, Node<E> succ) {
        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++;
    }
    
    //获取元素
    public E get(int index) {
        //检测元素索引
        checkElementIndex(index);
        return node(index).item;
    }
    
    //返回指定元素索引处的(非空)节点
    Node<E> node(int index) {
        //把链表分为两段,如果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;
        }
    }
    
    //省略。。。
    
}

总结:

LinkedList的get首先判断需要获取的数据在该链表的前半段还是后半段,这样可以减少需要遍历的数据,由于需要遍历数据,所以获取数据的速度会比ArrayList慢。
由于LinkedList底层是用双向链表实现的,没有初始化大小,也没有扩容的机制。

4 码农来洞见

4.1为什么ArrayList比LinkedList要快

ArrayListLinkedList本质上每次插入和删除元素都会进行复制数组的操作,如果ArrayList插入操作没有触发数组扩容操作,并且在List靠近末尾的地方插入,那么ArrayList只需要移动较少的数据,而LinkedList则需要一直查找到列表尾部,反而耗费较多时间,这时ArrayList就比LinkedList要快。

4.2 注意ArrayList不同JDK版本源码

JDK1.7中在调用构造器的时候数组长度就初始化为了10,JDK1.8则是在调用add方法后才创建数组长度为10的新数组替换默认空数组。

4.3 高并发下如何保证集合数据的同步

ArrayListLinkedList都不是线程安全的。然而Vector类被认为是过时的废弃的了。

方案一: Collections.synchronizedList(); 得到一个线程安全的 ArrayList。

方案二: concurrent 并发包下的 CopyOnWriteArrayList 类。

CopyOnWriteArrayList和Collections.synchronizedList是实现线程安全的列表的两种方式。两种实现方式分别针对不同情况有不同的性能表现,其中CopyOnWriteArrayList的写操作性能较差,而多线程的读操作性能较好。而Collections.synchronizedList的写操作性能比CopyOnWriteArrayList在多线程操作的情况下要好很多,而读操作因为是采用了synchronized关键字的方式,其读操作性能并不如CopyOnWriteArrayList。因此在不同的应用场景下,应该选择不同的多线程安全实现类。

4.4 为什么Java的Vector类被认为是过时的或者废弃的

Vector中对每一个独立操作都实现了同步,这通常不是我们想要的做法。对单一操作实现同步通常不是线程安全的(举个例子,比如你想遍历一个Vector实例。你仍然需要申明一个锁来防止其他线程在同一时刻修改这个Vector实例。如果不添加锁的话

通常会在遍历实例的这个线程中导致一个ConcurrentModificationException)同时这个操作也是十分慢的(在创建了一个锁就已经足够的前提下,为什么还需要重复的创建锁)

当然,即使你不需要同步,Vector也是有锁的资源开销的。

到此这篇关于Java中ArrayList和LinkedList区别的文章就介绍到这了,更多相关ArrayList和LinkedList区别内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java 中ArrayList与LinkedList性能比较

    java 中ArrayList与LinkedList性能比较 今天看一框架的代码,看到有些 可以使用ArrayList的地方 使用的是 LinkedList,用到的情景是在一个循环里面进行顺序的插入操作. 众所周知java里面List接口有两个实现ArrayList 和 LinkedList,他们的实现原理分别是c语言中介绍的数组和链表. 正如学习数据结构时的认识,对于插入操作,链表的结构更高效,原因是可以通过修改节点的指针 就可以完成插入操作, 而不像数组, 需要把插入位置之后的数组元素依次后

  • java中ArrayList和LinkedList的区别详解

    ArrayList和LinkedList都实现了List接口,有以下的不同点: 1.ArrayList是基于索引的数据接口,它的底层是数组.它可以以O(1)时间复杂度对元素进行随机访问.与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n). 2.相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者

  • java 集合之实现类ArrayList和LinkedList的方法

    List 的方法列表 方法名 功能说明 ArrayList() 构造方法,用于创建一个空的数组列表 add(E e) 将指定的元素添加到此列表的尾部 get(int index) 返回此列表中指定位置上的元素 size() 返回此列表中的元素数 clear() 移除此列表中的所有元素 isEmpty() 如果此列表中没有元素,则返回true remove(int index) 移除此列表中指定位置上的元素 indextof(Object o) 返回此列表中首次出现的指定元素的索引,或如果此列表不

  • Java中ArrayList和LinkedList的遍历与性能分析

    前言 通过本文你可以了解List的五种遍历方式及各自性能和foreach及Iterator的实现,加深对ArrayList和LinkedList实现的了解.下面来一起看看吧. 一.List的五种遍历方式 1.for each循环 List<Integer> list = new ArrayList<Integer>(); for (Integer j : list) { // use j } 2.显示调用集合迭代器 List<Integer> list = new Ar

  • Java集合框架之List ArrayList LinkedList使用详解刨析

    目录 1. List 1.1 List 的常见方法 1.2 代码示例 2. ArrayList 2.1 介绍 2.2 ArrayList 的构造方法 2.3 ArrayList 底层数组的大小 3. LinkedList 3.1 介绍 3.2 LinkedList 的构造方法 4. 练习题 5. 扑克牌小游戏 1. List 1.1 List 的常见方法 方法 描述 boolean add(E e) 尾插 e void add(int index, E element) 将 e 插入到 inde

  • java中ArrayList与LinkedList对比详情

    ArrayList,LinkedList都是Collection接口的通用实现方式,两者采用了不用的存储策略,用来适应不同场合的需要. 实现方式 ArrayList的内部采用集合的方式存储数据 唯一需要注意的是对于容量超过阈值的处理逻辑,数组的默认容量大小是10,最大容量是Integer.Max_Value,超过最大容量会抛内存溢出异常, 扩容机制看下面 扩容后的容量是原有容量的1.5倍 LinkedList的实现方式 内部采用双向链表Node内部类来存储数据,由于采用了双向链表,LinkedL

  • 区分Java中的ArrayList和LinkedList

    一:ArrayList和LinkedList的大致区别如下: 1.ArrayList是实现了基于动态数组的数据结构,ArrayList实现了长度可变的数组,在内存中分配连续的空间.遍历元素和随机访问元素的效率比较高 2.LinkedList基于链表的数据结构, 插入.删除元素时效率比较高  故:[插入.删除操作频繁时,可使用LinkedList来提高效率]LinkedList提供对头部和尾部元素进行添加和删除操作的方法,插入/删除第一个和最后一个效率比较高: 3:ArrayList和Linked

  • Java中ArrayList和LinkedList之间的区别_动力节点Java学院整理

    一.ArrayList ArrayList是一个可以处理变长数组的类型,这里不局限于"数"组,ArrayList是一个泛型类,可以存放任意类型的对象.顾名思义,ArrayList是一个数组列表,因此其内部是使用一个数组来存放对象的,因为Object是一切类型的父类,因而ArrayList内部是有一个Object类型的数组类存放对象.ArrayList类常用的方法有add().clear().get().indexOf().remove().sort().toArray().toStri

  • Java中ArrayList和LinkedList区别

    目录 1 前言 2 数据结构的区别 2.1 ArrayList 2.2 LinkedList 2.3 使用场景 3 源码分析 3.1 ArrayList核心源码 3.2 LinkedList核心源码 4 码农来洞见 4.1为什么ArrayList比LinkedList要快 4.2 注意ArrayList不同JDK版本源码 4.3 高并发下如何保证集合数据的同步 4.4 为什么Java的Vector类被认为是过时的或者废弃的 1 前言 许多语言,例如 Perl ,Python 和 Ruby ,都有

  • 分析Java中ArrayList与LinkedList列表结构的源码

    一.ArrayList源码分析(JDK7) ArrayList内部维护了一个动态的Object数组,ArrayList的动态增删就是对这个对组的动态的增加和删除. 1.ArrayList构造以及初始化 ArrayList实例变量 //ArrayList默认容量 private static final int DEFAULT_CAPACITY = 10; //默认空的Object数组, 用于定义空的ArrayList private static final Object[] EMPTY_ELE

  • java中ArrayList 、LinkList的区别分析

    1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构.      2.对于随机访问get和set,ArrayList优于LinkedList,因为ArrayList可以随机定位,而LinkedList要移动指针一步一步的移动到节点处.(参考数组与链表来思考)     3.对于新增和删除操作add和remove,LinedList比较占优势,只需要对指针进行修改即可,而ArrayList要移动数据来填补被删除的对象的空间. ArrayList和LinkedL

  • ArrayList和LinkedList区别及使用场景代码解析

    本文研究的主要是Java编程中ArrayList和LinkedList区别及使用场景的相关内容,具体介绍如下. 1.ArrayList是基于数组实现的,其构造函数为: private transient Object[] elementData; private int size; ArryList初始化时,elementData数组大小默认为10: 每次add()时,先调用ensureCapacity()保证数组不会溢出,如果此时已满,会扩展为数组length的1.5倍+1,然后用array.

  • Java中ArrayList的removeAll方法详解

    本文介绍的是关于Java中ArrayList的removeAll方法的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 在开发过程中,遇到一个情况,就是从所有骑手Id中过滤没有标签的骑手Id(直接查询没有标签的骑手不容易实现), List<Integer> allRiderIdList = new ArrayList(); // 所有的骑手,大致有23W数据 List<Integer> hasAnyTagRiderId = new ArrayList(); // 有标签

随机推荐