Java集合框架|Collection体系和核心源码详解(二)

本文详细分析了Java集合框架中的Vector和LinkedList。Vector类似于ArrayList,使用数组实现,但在添加和删除元素时采用同步控制,确保线程安全。LinkedList则基于双向链表,提供更高效的首尾元素操作,但查询速度相对较慢。文章通过实例展示了它们的构造方法、add和remove方法的源码分析,以及并发安全、继承层次和数据增长策略的比较。
摘要由CSDN通过智能技术生成

注:详解核心源码 = 逐句解释源码 + 分情况举例说明 + 必要辅助图解

目录

Vector实现类

Vector方法及案例实现

public class VectorTest {
    public static void main(String[] args) {
        //很多方法和ArrayList一样
        Vector vector=new Vector();
        vector.add("草莓");
        vector.add("香蕉");
        vector.add("西瓜");
        System.out.println("-----遍历-----");
        //Vector 特别的遍历方法是使用枚举器
        Enumeration el = vector.elements();
        while (el.hasMoreElements()){
            String str=(String) el.nextElement();
            System.out.printf("%s ",str);
        }
        System.out.println();
        System.out.println("-----firstElement/lastElement-----");
        System.out.println((String)vector.firstElement());
        System.out.println((String)vector.lastElement());
        System.out.println("-----elementAt-----");
        System.out.println(vector.elementAt(1));
    }
}
//输出结果:
-----遍历-----
草莓 香蕉 西瓜 
-----firstElement/lastElement-----
草莓
西瓜
-----elementAt-----
香蕉

Vector的源码分析(JDk1.8)

继承结构和层次关系

//Vector的继承关系和层次结构和ArrayList中的一模一样
public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
  ......
}

类中的属性

public class Vector<E>
    extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    /**
     * 保存vector中元素的数组,vector的容量是数组的长度,数组的长度最小值为vector的元素个数。
     * The array buffer into which the components of the vector are
     * stored. The capacity of the vector is the length of this array buffer,
     * and is at least large enough to contain all the vector's elements.
     * 任何在vector最后一个元素之后的数组元素是null
     * <p>Any array elements following the last element in the Vector are null.
     *
     * @serial
     */
    protected Object[] elementData;

    /**
     * vector实际元素的个数
     * The number of valid components in this {@code Vector} object.
     * Components {@code elementData[0]} through
     * {@code elementData[elementCount-1]} are the actual items.
     *
     * @serial
     */
    protected int elementCount;

    /**
     * vector需要自动扩容时增加的容量
     * 当vector的实际容量elementCount大于它的容量时,vector自动增加容量
     * 当capacityIncrement小于或等于0,vector的容量需要增长时将会成倍增长
     * The amount by which the capacity of the vector is automatically
     * incremented when its size becomes greater than its capacity.  If
     * the capacity increment is less than or equal to zero, the capacity
     * of the vector is doubled each time it needs to grow.
     *
     * @serial
     */
    protected int capacityIncrement;

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -2767605614048989439L;
}

构造方法

无参构造 Vector()
//构造一个指定容量为10,自增容量为0的空vector
public Vector() {
    this(10);
}
有参构造 Vector(int initialCapacity)
//构造一个指定容量为initialCapacity,自增容量为0的空vector
public Vector(int initialCapacity) {
    this(initialCapacity, 0);
}
有参构造 Vector(int initialCapacity, int capacityIncrement)
//构造一个指定容量为initialCapacity,自增容量为capacityIncrement的空vector
public Vector(int initialCapacity, int capacityIncrement) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}
有参构造 Vector(Collection<? extends E> c)
//使用指定的Collection构造vector
public Vector(Collection<? extends E> c) {
    elementData = c.toArray();
    elementCount = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

add方法的源码分析

  • Vector和ArrayList的数据结构都是数组,所以操作起来差不多,这里以add方法为例。
add(E)
情况1 无参初始化并添加第1个元素
/*
		Vector vector = new Vector(); 
		//调用了这个构造函数函数后
		//this.elementData = new Object[initialCapacity]; 即initialCapacity=10,elementData.length=10
		//this.capacityIncrement = capacityIncrement; 即capacityIncrement=0
		
		vector.add(xxx);
*/
//方法前面加了synchronized关键字,给该方法加锁了,哪个线程先调用它,其它线程就得等着,属于线程安全
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
/*
		进入ensureCapacityHelper(int minCapacity)函数
		其中initialCapacity=10,capacityIncrement=0,minCapacity=elementCount +1=0+1=1
*/
//这个方法是异步(也就是能被多个线程同时访问)的,原因是为了让同步方法都能调用到这个检测容量的方法,比如add的同时,另一个线程调用了add的重载方法,那么两个都需要同时查询 容量够不够,所以这个就不需要用synchronized修饰了。因为不会发生线程不安全的问题
private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    // minCapacity - elementData.length=1-10 < 0 所以不扩容
    if (minCapacity - elementData.length > 0)
      grow(minCapacity);
}
/*
		返回add(E e)函数 elementData[0] = e; elementCount自增 返回true
*/
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
情况2 无参初始化并添加第11个元素
/*
		Vector vector = new Vector(); 
		//调用了这个构造函数函数后
		//this.elementData = new Object[initialCapacity]; 即initialCapacity=10,elementData.length=10
		//this.capacityIncrement = capacityIncrement; 即capacityIncrement=0
		
		vector.add(xxx);
		:
		:
		vector.add(xxx);
		vector.add(xxx);当添加第11个元素时
*/
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
/*
		添加第11个元素,进入ensureCapacityHelper(int minCapacity)函数
		其中initialCapacity=10,capacityIncrement=0,minCapacity=elementCount +1=10+1=11
*/
private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    // minCapacity - elementData.length=11-10 > 0 所以grow函数扩容
    if (minCapacity - elementData.length > 0)
      grow(minCapacity);
}
/*
		添加第11个元素,进入grow(int minCapacity)函数
		此时initialCapacity=10,capacityIncrement=0,minCapacity=11
*/
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;//oldCapacity=10
    // 因为capacityIncrement=0 所以newCapacity=10+10=20
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    // newCapacity - minCapacity >0
    if (newCapacity - minCapacity < 0)
      newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
      newCapacity = hugeCapacity(minCapacity);
  	//扩容数组
    elementData = Arrays.copyOf(elementData, newCapacity);
}
/*
		返回add(E e)函数 elementData[0] = e; elementCount自增 返回true
*/
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
情况3 有参初始化Vector(8,6)并添加第9个元素
/*
		Vector vector = new Vector(8,6); 
		//调用了这个构造函数函数后
		//this.elementData = new Object[initialCapacity]; 即initialCapacity=8,elementData.length=8
		//this.capacityIncrement = capacityIncrement; 即capacityIncrement=6
		
		vector.add(xxx);
		:
		:
		vector.add(xxx);
		vector.add(xxx);当添加第9个元素时
*/
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
/*
		添加第9个元素,进入ensureCapacityHelper(int minCapacity)函数
		其中initialCapacity=8,capacityIncrement=6,minCapacity=elementCount +1=8+1=9
*/
private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    // minCapacity - elementData.length=9-8 > 0 所以grow函数扩容
    if (minCapacity - elementData.length > 0)
      grow(minCapacity);
}
/*
		添加第9个元素,进入grow(int minCapacity)函数
		此时initialCapacity=8,capacityIncrement=6,minCapacity=9
*/
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;//oldCapacity=8
    // 因为capacityIncrement=6>0 所以newCapacity=8+6=14
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    // newCapacity - minCapacity >0
    if (newCapacity - minCapacity < 0)
      newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
      newCapacity = hugeCapacity(minCapacity);
  	//扩容数组
    elementData = Arrays.copyOf(elementData, newCapacity);
}

remove方法的源码分析

  • Vector的removeAll和retainAll方法有点不一样。
removeAll(Collection<?> c)
//removeAll和retainAll差不多,主要说removeAll
public synchronized boolean removeAll(Collection<?> c) {
    return super.removeAll(c);
}
/*
		把集合c传给父类的removeAll函数
*/
public boolean removeAll(Collection<?> c) {
    //检查该对象是不是空的
    Objects.requireNonNull(c);
    //默认false
    boolean modified = false;
  	//创建iterator迭代器
    Iterator<?> it = iterator();
  	//while循环一个个元素进行遍历,每次循环如果集合c中包含这个元素就remove掉,modified设置为true
    while (it.hasNext()) {
      if (c.contains(it.next())) {
        it.remove();
        modified = true;
      }
    }
    return modified;
}

LinkedList实现类

LinkedList方法及案例实现

public class LinkedListTest {
    public static void main(String[] args) {
        LinkedList ll=new LinkedList();
        Dog dog1=new Dog("小花",3);
        Dog dog2=new Dog("跳跳",4);
        ll.add(dog1);
        ll.add(dog2);
        System.out.println("-----addFirst-----");
        //addFirst 在列表的首部添加元素
        System.out.println("添加前:"+ll);
        ll.addFirst(new Dog("小宝",2));
        System.out.println("添加后:"+ll);

        System.out.println("-----addLast-----");
        //addLast 在列表的尾部添加元素
        ll.addLast(new Dog("虎仔",6));
        System.out.println("添加后:"+ll);

        System.out.println("-----getFirst/getLast-----");
        //返回列表中第一个/最后一个元素
        System.out.println(ll.getFirst());
        System.out.println(ll.getLast());

        System.out.println("-----removeFirst/removeLast-----");
        //删除并返回列表中的第一个/最后一个元素
        ll.removeFirst();
        System.out.println(ll);
        ll.removeLast();
        System.out.println(ll);
    }
}
class Dog{
    private String name;
    private int age;

    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }
}
//输出结果:
-----addFirst-----
添加前:[Dog{name='小花', age=3}, Dog{name='跳跳', age=4}]
添加后:[Dog{name='小宝', age=2}, Dog{name='小花', age=3}, Dog{name='跳跳', age=4}]
-----addLast-----
添加后:[Dog{name='小宝', age=2}, Dog{name='小花', age=3}, Dog{name='跳跳', age=4}, Dog{name='虎仔', age=6}]
-----getFirst/getLast-----
Dog{name='小宝', age=2}
Dog{name='虎仔', age=6}
-----removeFirst/removeLast-----
[Dog{name='小花', age=3}, Dog{name='跳跳', age=4}, Dog{name='虎仔', age=6}]
[Dog{name='小花', age=3}, Dog{name='跳跳', age=4}]

LinkedList的数据结构

  • LinkedList底层使用的双向链表结构,双向链表意味着我们可以从头开始正向遍历,或者是从尾开始逆向遍历,并且可以针对头部和尾部进行相应的操作。

LinkedList的源码分析(JDK1.8)

继承关系和层次关系

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
	......
}
为什么LinkedList比ArrayList多了一层AbstractSequentialList的抽象类
  • 首先,我们先要区分顺序存取和随机存取。

    • 随机存取,当存储器中的数据被读取或写入时,所需要的时间与该数据所在的物理地址无关,常见的就是我们平时在用的数组。
    • 顺序存取,所需要的时间与该数据所在的物理地址有关,表现为存取第N个数据时,必须先访问前(N-1)个数据,常见的就是我们平时在用的链表。
  • 其次,我们要知道抽象的概念,越高层的类比如说Object类就越抽象,越低层的类就越有属于自己的独特的特性。

  • 所以,使用AbstractSequentialList抽象类目的是抽象出类似LinkedList这种类的一些共同的方法,方便操作实现。

LinkedList实现了哪些接口
  • List接口:列表,add、set、等一些对列表进行操作的方法。
  • Deque接口:实现队列的各种特性。
  • Cloneable接口:实现了该接口,就可以使用Object.Clone()方法了。
  • Serializable接口:能够序列化。

类的属性

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
  	/*
  		实际元素的大小
  	*/
    transient int size = 0;

    /**
     * 头指针
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;

    /**
     * 尾指针
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;
  	//transient 不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化
}

构造方法

无参构造方法 LinkedList()
public LinkedList() {}
有参构造方法 LinkedList(Collection<? extends E> c)
//将集合c中的各个元素构建成LinkedList链表
public LinkedList(Collection<? extends E> c) {
    //调用无参构造函数
    this();
    //添加集合中所有的元素
    addAll(c);
}

内部类(Node)

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

add方法的源码分析

add(E)
情况1 无参构造并添加第1个元素
/*
	  LinkedList ll=new LinkedList();
	  ll.add(“苹果”);
*/
public boolean add(E e) {
    linkLast(e);
    return true;
}
/*
		进入linkLast(e)这个函数
*/
void linkLast(E e) {
		//创建一个为l的结点指针,指向last指向的位置
    final Node<E> l = last;
  	//创建一个新的结点,这个结点的值为e,前驱指向l指向的位置,后继指向null
    final Node<E> newNode = new Node<>(l, e, null);
  	//last结点指针指向新结点newNode
    last = newNode;
  	//如果l指向null,first结点指针指向新结点newNode,否则l结点指针的后继指向新结点newNode
    if (l == null)
      first = newNode;
    else
      l.next = newNode;
  	//添加一个节点,size自增
    size++;
    modCount++;
}

情况2 无参构造并添加第2个元素
/*
	  LinkedList ll=new LinkedList();
	  ll.add(“苹果”);
	  ll.add(“香蕉”);
*/
public boolean add(E e) {
    linkLast(e);
    return true;
}
/*
		添加第2个元素进入linkLast(e)这个函数
*/
void linkLast(E e) {
		......
}

addAll(Collection<? extends E>)/addAll(int, Collection<? extends E> )
情况1 将集合c中的各个元素构建成链表添加到空集合linked尾部
/*
		LinkedList newLinked=new LinkedList();
    newLinked.add("苹果");
    newLinked.add("香蕉");
    LinkedList linked=new LinkedList();
    linked.addAll(newLinked);
*/
public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}
/*
    进入addAll(int index, Collection<? extends E> c)
    其中index=size 集合c就是newLinked
*/
public boolean addAll(int index, Collection<? extends E> c) {
    //检查index这个是否为合理
    checkPositionIndex(index);
    //将集合c转换为Object数组 a
    Object[] a = c.toArray();
    //数组a的长度numNew,也就是由多少个元素
    int numNew = a.length;
    if (numNew == 0)
      return false;//集合c是个空的,直接返回false,什么也不做
		//创建两个指针结点pred和succ
    Node<E> pred, succ;
  	//这里index=size succ = null; pred = last = null;
    if (index == size) {
      succ = null;
      pred = last;
    } else {
      succ = node(index);
      pred = succ.prev;
    }
		//增强for遍历数组a中的元素
    for (Object o : a) {
      @SuppressWarnings("unchecked") E e = (E) o;
      //将每个元素封装为结点
      Node<E> newNode = new Node<>(pred, e, null);
      //第一次循环:将"苹果"封装成结点,因为pred=null,所以first指向数据域是"苹果"这个newNode
      //第二次循环:将"香蕉"封装成结点,因为pred != null,所以pred的后继指向数据域是"香蕉"这个newNode
      if (pred == null)
        first = newNode;
      else
        pred.next = newNode;
      //第一次循环:然后pred指向数据域是"苹果"这个newNode
      //第二次循环:然后pred指向数据域是"香蕉"这个newNode
      pred = newNode;
    }
		//succ=null,所以pred指向就是last指向,在这里就是指向数据域是"香蕉"这个结点
    if (succ == null) {
      last = pred;
    } else {
      pred.next = succ;
      succ.prev = pred;
    }
		//增加了几个元素,就增加多少size
    size += numNew;
    modCount++;
    return true;
}

情况2 将集合c中的各个元素构建成链表添加到已经有一个元素的linked集合尾部
/*
    LinkedList newLinked=new LinkedList();
    newLinked.add("苹果");
    newLinked.add("香蕉");
    LinkedList linked=new LinkedList();
    linked.add("西瓜");
    linked.addAll(newLinked);
*/
public boolean addAll(Collection<? extends E> c) {
    return addAll(size, c);
}
/*
    进入addAll(int index, Collection<? extends E> c)
    其中index=size 集合c就是newLinked
*/
public boolean addAll(int index, Collection<? extends E> c) {
    //检查index这个是否为合理
    checkPositionIndex(index);
    //将集合c转换为Object数组 a
    Object[] a = c.toArray();
    //数组a的长度numNew,也就是由多少个元素
    int numNew = a.length;
    if (numNew == 0)
      return false;//集合c是个空的,直接返回false,什么也不做
		//创建两个指针结点pred和succ
    Node<E> pred, succ;
  	//这里index=size succ = null; pred = last = 最后一个结点(即数据域为西瓜的结点);
    if (index == size) {
      succ = null;
      pred = last;
    } else {
      succ = node(index);
      pred = succ.prev;
    }
		//增强for遍历数组a中的元素
    for (Object o : a) {
      @SuppressWarnings("unchecked") E e = (E) o;
      //将每个元素封装为结点
      Node<E> newNode = new Node<>(pred, e, null);
      //第一次循环:将"苹果"封装成结点,因为pred != null,所以pred的后继指向数据域是"苹果"这个newNode
      //第二次循环:将"香蕉"封装成结点,因为pred != null,所以pred的后继指向数据域是"香蕉"这个newNode
      if (pred == null)
        first = newNode;
      else
        pred.next = newNode;
      //第一次循环:然后pred指向数据域是"苹果"这个newNode
      //第二次循环:然后pred指向数据域是"香蕉"这个newNode
      pred = newNode;
    }
		//succ=null,所以pred指向就是last指向,在这里就是指向数据域是"香蕉"这个结点
    if (succ == null) {
      last = pred;
    } else {
      pred.next = succ;
      succ.prev = pred;
    }
		//增加了几个元素,就增加多少size
    size += numNew;
    modCount++;
    return true;
}

情况3 将集合c中的各个元素构建成结点依次添加到已经有三个元素的linked集合内部
/* 
    LinkedList newLinked=new LinkedList();
    newLinked.add("苹果");
    LinkedList linked=new LinkedList();
    linked.add("西瓜");
    linked.add("葡萄");
    linked.add("香蕉");
    linked.addAll(1,newLinked);
*/
//此时index=1
public boolean addAll(int index, Collection<? extends E> c) {
    //检查index这个是否为合理
    checkPositionIndex(index);
    //将集合c转换为Object数组 a
    Object[] a = c.toArray();
    //数组a的长度numNew,也就是由多少个元素
    int numNew = a.length;
    if (numNew == 0)
      return false;//集合c是个空的,直接返回false,什么也不做
		//创建两个指针结点pred和succ
    Node<E> pred, succ;
  	//这里index!=size succ = null; pred = last = 最后一个结点(即数据域为西瓜的结点);
    if (index == size) {
      succ = null;
      pred = last;
    } else {
      succ = node(index); //node函数见最下面,返回索引为index的这个结点,然后让succ指向这个结点
      pred = succ.prev; //pred指向succ前一个结点
    }
		//增强for遍历数组a中的元素
    for (Object o : a) {
      @SuppressWarnings("unchecked") E e = (E) o;
      //将每个元素封装为结点
      Node<E> newNode = new Node<>(pred, e, null);
      //第一次循环:将"苹果"封装成结点,因为pred != null,所以pred的后继指向数据域是"苹果"这个newNode
      if (pred == null)
        first = newNode;
      else
        pred.next = newNode;
      //第一次循环:然后pred指向数据域是"苹果"这个newNode
      pred = newNode;
    }
		//succ!=null,所以pred的后继指向succ指向的位置,succ的前驱指向pred指向的位置
    if (succ == null) {
      last = pred;
    } else {
      pred.next = succ;
      succ.prev = pred;
    }
		//增加了几个元素,就增加多少size
    size += numNew;
    modCount++;
    return true;
}

//node函数
Node<E> node(int index) {
    // assert isElementIndex(index);
    // 判断插入的位置在链表前半段或者是后半段
    // index < (size >> 1)就是index<size/2
    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; //返回该结点
    }
}

在addAll函数中,为什么要先转化为数组再进行遍历,而不是直接遍历集合呢?
  • 如果直接遍历集合的话,那么在遍历过程中需要插入元素,在堆上分配内存空间,修改指针域,这个过程中就会一直占用着这个集合,考虑同步的话,其他线程只能一直等待。
  • 如果转化为数组,只需要遍历集合,而遍历集合过程中不需要额外的操作,所以占用的时间相对是较短的,这样就利于其他线程尽快的使用这个集合。说白了,就是有利于提高多线程访问该集合的效率,尽可能短时间的阻塞。

remove方法的源码分析

remove(Object)
public boolean remove(Object o) {
  	//这里可以看到,linkedList也能存储null
    if (o == null) {
        //循环遍历链表,直到找到null值,然后使用unlink移除该值。下面的这个else中也一样
        //下面for循环的遍历,如果我们要移除的值在链表中存在多个一样的值,
        //那么我们会移除index最小的那个,也就是最先找到的那个值
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x); //调用unlink函数删除该结点
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}

//unlink函数
E unlink(Node<E> x) {
    // assert x != null;
  	// 拿到节点x的三个属性
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;		
		//这里开始往下就进行移除该元素之后的操作
    if (prev == null) {
      //如果prev为空,说明删除的是头结点,就把first结点指针指向后一个结点
      first = next;
    } else {
      //如果prev不为空,就把要删除结点的前一个结点的后继指向要删除结点的后一个结点
      //比如说有1、2、3,就将1.next指向3
      prev.next = next; 
      //让x的前驱指向null
      x.prev = null;
    }

    if (next == null) {
      //如果next为空,说明删除的是尾结点,就把last结点指针指向前一个结点
      last = prev;
    } else {
      //如果next不为空,就把要删除结点的后一个结点的前驱指向要删除结点的前一个结点
      //比如说有1、2、3,就将3.prev指向1
      next.prev = prev;
      //让x的后继指向null
      x.next = null;
    }
    
  	//把x节点的值赋为空
    x.item = null;
  	//size减1
    size--;
    modCount++;
    return element;
}

ArrayList、LinkedList、Vector的区别

从存储数据结构分析

  • ArrayList:数组;Vector:数组;LinkedList:双向链表。

  • 数组可以根据下标快速查找,所以大部分情况下,查询快,但是如果要进行增删操作的时候,会需要移动修改元素后面的所有元素,所以增删的开销比较大,数组的对增删操作的执行效率低。

  • 链表增加和删除元素方便,增加或删除一个元素,仅需处理结点间的引用即可,但是查询不方便,需要一个个对比,无法根据下标直接查找。

从并发安全上分析

  • Vector:线程安全;ArrayList:非线程安全;LinkedList:非线程安全。

从继承上分析

  • ArrayList和Vector一样,LinkedList比前面两者多了一层AbstractSequentialList的抽象类,抽象出类似LinkedList这种类的一些共同的方法,方便操作实现。

从数据增长分析

  • Vector:默认情况下增长为原数组长度的1倍。

  • ArrayList:默认增长为原数组的1.5倍。

Hi, welcome to JasperのBlog

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值