ArrayList与LinkedList的区别

一 概述

ArrayList与LinkedList都是对List接口的实现,然而在实现List的过程中实际上是基于数组和链表的区别。

数组:用于处理一组数据类型相同的数据,但是不允许动态定义数组的大小,即在使用数组之前必须确定数组的大小。当实际数据量过少的时候会存在不使用的内存空间,从而造成内存空间的浪费。当然,当数据量过大的时候也会存在超出数组定义的元素个数,造成数组越界。在操作方面,数组插入和删除数据是需要进行数据移动的,效率较低,但是在查找和更新数据的时候因为存在下标索引,所以效率较高。

对于数组分配内存空间,数组元素是保存在内存中的栈区,所以系统会自动申请空间,而且数组元素的内存空间是连续的。

链表:链表是一种可以动态分配内存空间的数据结构,可以适应数据动态变化的情况,而且可以通过指针方便的实现各个数据结点之间的关系,所以可以高效的实现数据的增减。而对于链表中数据的查找,需要从前往后遍历一遍,所以效率比较低。

对于链表分配内存空间,链表数据是保存在内存的堆空间中,每个数据结点的空间需要手动申请,操作比较麻烦。但是链表中的数据空间不是连续存在的。

二 ArrayList

ArrayList类的定义:继承了抽象类AbstractList和实现了RandomAccess,Cloneable和Serializable接口

    public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

//AbstractList抽象类的定义

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>

//List的接口定义

public interface List<E> extends Collection<E>

//Collection接口的实现类之一Collections中的binarySearch方法

   //binarySearch方法源码
public static <T>
   int binarySearch(List<? extends Comparable<? super T>> list, T key) {
        // instanceof判断对象是否属于某个类或接口类型
       if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
           return Collections.indexedBinarySearch(list, key);
       else
           return Collections.iteratorBinarySearch(list, key);
   }
 //binarySearch方法注释

   //二分查找

 Searches the specified list for the specified object using the binary
 * search algorithm.  The list must be sorted into ascending order
 * according to the {@linkplain Comparable natural ordering} of its
 * elements (as by the {@link #sort(List)} method) prior to making this
 * call.  If it is not sorted, the results are undefined.  If the list
 * contains multiple elements equal to the specified object, there is no
 * guarantee which one will be found.

     //方法说明

 * <p>This method runs in log(n) time for a "random access" list (which
 * provides near-constant-time positional access).  If the specified list
 * does not implement the {@link RandomAccess} interface and is large,
 * this method will do an iterator-based binary search that performs
 * O(n) link traversals and O(log n) element comparisons. */

根据二分查找说明可知:binarySearch(二分查找),对一个有序序列的查找算法,时间复杂度为O(logn),方法注释中强调二分查找的序列必须为有序序列。

根据方法说明可知:实现RandomAccess接口的List满足O(logn)的时间复杂度完成搜索。而未实现RandomAccess接口的List的时间复杂度为O(n)的查找时间复杂度加上O(logn)的比较时间复杂度,即时间复杂为O(nlogn)。

RandomAccess接口:

public interface RandomAccess {
                 //空的接口
        }

RandomAccess是一个Marker(标记接口),只起标记作用,根据binarySearch方法中的(list instanceof RandomAccess)可知,实现RandomAccess接口的List实现二分查找是采用方法Collections.indexedBinarySearch(list, key),否则采用的二分查找方法为Collections.iteratorBinarySearch(list, key);

由源码发现实现RandomAccess接口的List集合采用一般的for循环遍历,而未实现这接口则采用迭代器。

indexedBinarySearch(list,key)(使用for循环效率比较高,使用list.get(mid)),因为ArrayList.get(index)时间复杂度:O(1),按索引下标查找。

   private static <T>
    int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
        int low = 0;
        int high = list.size()-1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            Comparable<? super T> midVal = list.get(mid);
            int cmp = midVal.compareTo(key);

            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found
    }

//Cloneable接口

public interface Cloneable {
      //空接口
}

Cloneable接口同样为一个标记接口,只有实现了该接口,然后在类中重写Object中的clone方法,并通过类调用clone方法才能古进行克隆。否则会抛出CloneNotSupportedException。

//Serializable接口

 

public interface Serializable {
      //空接口
}

Serializable接口同样为标记接口,使得某类可以实现序列化,序列化的目的时将一个实现了Serializable接口的对象转换为字节序列。从而实现不同进程之间的远程通信

三 LinkedList

LinkedList类的定义:继承了抽象类AbstractSequentialList和实现了Deque,Cloneable和Serializable接口

 

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable

未实现RandomAccess接口的实现类使用方法iteratorBinarySearch(list,key)(使用迭代器效率比较高,使用get(i,mid)),因为LinkedList.get(index)时间复杂度:O(n) ,每次都从链表的第一个数据开始查找。

 private static <T>
    int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
    {
        int low = 0;
        int high = list.size()-1;
        ListIterator<? extends Comparable<? super T>> i = list.listIterator();

        while (low <= high) {
            int mid = (low + high) >>> 1;
            Comparable<? super T> midVal = get(i, mid);
            int cmp = midVal.compareTo(key);

            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found
    }

get(i ,mid)(该方法避免链表实现二分查找时每次都从表头开始找,而是根据上次查找的结果继续查找)

  /**
     * Gets the ith element from the given list by repositioning the specified
     * list listIterator.
     */
    private static <T> T get(ListIterator<? extends T> i, int index) {
        T obj = null;
        int pos = i.nextIndex();
        if (pos <= index) {
            do {
                obj = i.next();
            } while (pos++ < index);
        } else {
            do {
                obj = i.previous();
            } while (--pos > index);
        }
        return obj;
    }

Deque接口:

public interface Deque<E> extends Queue<E> {

    //Deque methods
    void addFirst(E e);
    void addLast(E e);
    boolean offerFirst(E e);
    boolean offerLast(E e);
    E removeFirst();
    E removeLast();
    E pollFirst();
    E pollLast();
    E getFirst();
    E getLast();
    E peekFirst();
    E peekLast();
    boolean removeFirstOccurrence(Object o);
    boolean removeLastOccurrence(Object o);
    
    //Queue methods
    boolean add(E e);
    boolean offer(E e);
    E remove();        
    E poll();
    E element();
    E peek();
    
    //Stack methods
    void push(E e);
    E pop();

    //Collection methods
    boolean remove(Object o);
    boolean contains(Object o);
    public int size();
    Iterator<E> iterator();
    Iterator<E> descendingIterator();
}

Deque为双端队列。双端队列可以在队列两端都可以进行插入和删除。Deque是一个比StackQueue功能更强大的接口,它同时实现了栈和队列的功能。ArrayDequeLinkeList实现了Deque接口。

Deque既可以作为先进后出的栈数据结构,也可以作为先进先出的队列数据结构。

四 ArrayList与LinkedList的区别

  1. 数据结构的不同:ArrayList是基于动态数组实现,类同于动态数组,如果没有指定数组的大小,则默认初始化的大小为10的数组,但数据超过10的时候,数组无法保存多余的数据时,系统会另外申请一个长度为当前长度1.5倍的数组,然后会将之前的数据拷贝到新建的数组中。而LinkedList是带头结点和尾结点的双向链表结构,可以作堆栈,队列,双向队列。
  2. 不同操作的效率不同:当随机访问List的时候,即实现get和set操作,由于ArrayList可以直接使用索引,而LinkedList基于链表实现,需要移动指针进行一次查找。所以ArrayList的效率更高。当删除或者新增数据时,即实现delete和add操作,由于ArrayList需要移动数据所以效率会比较差。
  3. 数据结构所消耗内存空间的不同:ArrayList主要是存储数据的空间,而LinkedList不仅需要存储数据的空间,而且需要保存结点指针信息的空间。
  4. 实际的应用场景不同:ArrayList适合频繁查找和更新的应用场景,LinkedList适合频繁插入和删除的应用场景。
  5. 实现的接口有差别:ArrayList实现标记接口RandomAccess,而LinkedList却没有实现。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值