Java集合系列---List源码解析(ArrayList和LinkedList的区别)

List源码主要讲ArrayList,LinkedList,Vector三个类

1 ArrayList

ArrayList是一个底层基于数组的集合,
首先来看一下它的继承关系,

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
        // 版本号
    private static final long serialVersionUID = 8683452581122892189L;
    // 缺省容量
    private static final int DEFAULT_CAPACITY = 10;
    // 空对象数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
    // 缺省空对象数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    // 元素数组
    transient Object[] elementData;
    // 实际元素大小,默认为0
    private int size;
    //集合的最大长度
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

1)构造方法有三种:

1)无参构造

public ArrayList() {
// DEFAULTCAPACITY_EMPTY_ELEMENTDATA也是个空的数组
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

2)有参构造

 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);
        }
    }

3)参数为一个继承了Collection的类

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();//将给定的集合数组化
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)//判断elementData是不是Object类型
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

2)核心方法

a 添加元素

public boolean add(E e) {
		//判断数组是否会越界,
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    //用于扩展数组的函数
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    	//判断数组是否为空,为空的话直接返回默认的10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //否则的话返回当前所需长度
        return minCapacity;
    }
    private void ensureExplicitCapacity(int minCapacity) {
    	//增加修改次数
        modCount++;

        // overflow-conscious code
        //如果当前所需长度大于数组容量,给数组扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //通过位右移将新数组容量扩充为原来的1.5倍
        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);
    }

2 删除元素

 public E remove(int index) {
 		//检查下标是否越界
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)//判断是否数组中就一个数据
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
         //将最后一个元素清空
        elementData[--size] = null; // clear to let GC do its work
        return oldValue;
    }

2 Vector

也是基于动态数组来实现的,跟ArrayList很像,但是它是线程安全的,也就是在大部分的方法上都加上了synchronized因此效率较低

3 LinkedList

LinkedList类声明如下:

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

可以发现 LinkedList继承了 AbstractSequentialList抽象类,而不是像 ArrayList和 Vector那样实现 AbstractList,实际上,java类库中只有 LinkedList继承了这个抽象类,正如其名,它提供了对序列的连续访问的抽象:
LinkedList的底层是 Deque双向链表,实现了 Deque接口,而 Deque接口继承于 Queue接口,因此,在java中,如果要实现队列,一般都使用 LinkedList来实现。
节点内部类

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;
        }
    }

集合的元素

//集合元素个数
transient int size = 0;
//头结点
transient Node<E> first;
//尾节点
transient Node<E> last;

构造器

//无参构造器
public LinkedList() {}
 
//传入外部集合的构造器
public LinkedList(Collection<? extends E> c) {
  this();
  addAll(c);
}

核心方法:
构造时的addAll方法

 public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);//判断index是否越界,越界则抛出异常
		//index就是当前的数组长度
        Object[] a = c.toArray();//将集合数组化
        int numNew = a.length;
        if (numNew == 0)
            return false;

        Node<E> pred, succ;//声明pred和succ两个Node对象,用于标识要插入元素的前一个节点和最后一个节点
        if (index == size) {//如果size等于原数组长度则表示在结尾添加
            succ = null;
            pred = last;//last是尾结点
        } else {
            succ = node(index);//index位置上的Node对象
            pred = succ.prev;
        }

        for (Object o : a) {//遍历要插入的集合
            @SuppressWarnings("unchecked") E e = (E) o;
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                first = newNode;//如果要插入的位置的前一个节点为null表示是第一个节点,则直接将newNode赋给第一个节点
            else
                pred.next = newNode;//将要插入的集合元素节点对象赋给此位置原节点对象的前一个对象的后一个,即更改前一个节点对象的next指针指到新插入的节点上
            pred = newNode;//更改指向后将新节点对象赋给pred作为下次循环中新插入节点的前一个对象节点,依次循环
        }
//此时pred代表集合元素的插入完后的最后一个节点对象
        if (succ == null) {//结尾添加的话在添加完集合元素后将最后一个集合的节点对象pred作为last
            last = pred;
        } else {
            pred.next = succ;//将集合元素的最后一个节点对象的next指针指向原index位置上的Node对象
            succ.prev = pred;//将原index位置上的pred指针对象指向集合的最后一个对象
        }

        size += numNew;
        modCount++;
        return true;
    }

找到index位置的node

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

       if (index < (size >> 1)) {//判断index是否小于size的一半,如果小于则从头遍历节点,否则从结尾遍历节点
           Node<E> x = first;
           for (int i = 0; i < index; i++)
               x = x.next;//从first第一个节点开始,依次将后一个节点赋给x
           return x;//返回index位置上的Node对象,下同理
       } else {
           Node<E> x = last;
           for (int i = size - 1; i > index; i--)
               x = x.prev;
           return x;
       }
   }

当不指定index往LinkedList中添加元素的时候默认是在链表的尾部添加数据

 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
        //插入之前的尾节点的nextNode指向新插入的节点
            l.next = newNode;
            //长度加一
        size++;
        //修改次数+1
        modCount++;
    }

当指定index插入时,会先查询出index位置的元素,然后调用linkBefore将此元素插入到查询到的元素的前一个位置。

public void add(int index, E element) {
       checkPositionIndex(index);

       if (index == size)
           linkLast(element);
       else
           linkBefore(element, node(index));
   }

linkBefore源码

void linkBefore(E e, Node<E> succ) {
       // assert succ != null;
       final Node<E> pred = succ.prev;
       //新建节点,并将preNode指向idnex-1节点
       final Node<E> newNode = new Node<>(pred, e, succ);
       //插入前的index节点的prev指向新节点
       succ.prev = newNode;
       if (pred == null)
           first = newNode;
       else
          //index-1节点的nextNode指向新节点
           pred.next = newNode;
       size++;
       modCount++;
   }

remove方法

public E remove(int index) {
		//下标越界检查
        checkElementIndex(index);
        //unlink 断开指定节点的联系
        return unlink(node(index));
    }
    E unlink(Node<E> x) {
  //假设x不是null
  // assert x != null;
  //定义一个变量element接受x节点中的元素 最后会最后返回值返回
  final E element = x.item;
  //定义连个节点分别获得x节点的前后节点引用
  final Node<E> next = x.next;
  final Node<E> prev = x.prev;
  //如果节点前引用为null 说明这是第一个节点
  if (prev == null) {
    //x是第一个节点 即将被删除  那么first需要被重新赋值
    first = next;
  } else {
    //如果不是x不是第一个节点  将prev(x的前一个节点)的next指向x的后一个节点(绕过x)
    prev.next = next;
    //x的前引用赋值null
    x.prev = null;
  }
//如果节点后引用为null 说明这是最后一个节点  一系列类似前引用的处理方式 不再赘述
  if (next == null) {
    last = prev;
  } else {
    next.prev = prev;
    x.next = null;
  }
//将x节点中的元素赋值null
  x.item = null;
  size--;
  modCount++;
  return element;
}

对比ArrayList和LinkedList的区别,LinkedList主要依赖于一个双端对列,在添加和删除元素方面很有优势,但是在查询方面还是ArrayList效率更高

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值