Java 集合框架系列(二):List 接口及其实现类

1. List 接口

继承 Collection 接口的一个接口,用来表示传统的线性表的结构,实现该接口的结构存储的数据不唯一(值不唯一,类型是唯一的),数据按照添加的顺序进行排列。
其主要扩展 Collection 的方法有:

public interface List<E> extends Collection<E> {
     // 元素进行排序
     default void sort(Comparator<? super E> c) {
         Object[] a = this.toArray();
         Arrays.sort(a, (Comparator) c);
         ListIterator<E> i = this.listIterator();
         for (Object e : a) {
             i.next();
             i.set((E) e);
         }
     }
     
     // 获取某个下标的元素
     E get(int index);
     
     // 修改某个下标的元素
     E set(int index, E element);
     
     // 判断元素是否存在
     // 从头开始,并返回第一相同元素的索引
     int indexOf(Object o);
     // 从后面开始,并返回第一个相同元素的索引
     int lastIndexOf(Object o);
     
     // 快速创建List对象的静态方法
     static <E> List<E> of() {
         return (List<E>) ImmutableCollections.ListN.EMPTY_LIST;
     }
     static <E> List<E> of(E e1) {
         return new ImmutableCollections.List12<>(e1);
     }
     // 还有好几个重载的方法
     ...
 }

2. 实现类

2.1 Vector

底层用数组存储,里面的方法都是线程安全的,现版本基本被抛弃了,锁的粒度比较大,性能低,不建议使用。

  1. 类的定义及关键字段:
  • 用数组来保存数据。
  • 默认数组元素初始化大小为10。
  • capacityIncrement 当为0的时候默认扩大一倍的大小。
public class Vector<E> extends AbstractList<E>
     implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
     // 1. 重要的属性
     // 用数组结构来保存数据
     protected Object[] elementData;
     // 数组中元素的个数
     protected int elementCount;
     // 扩容时数组增长的大小,如果该值小于等于0时会默认增长一倍
     protected int capacityIncrement;
     
     //-----------------------------------------------------
     // 2.构造函数
     public Vector() {
         // 无参构造默认数组初始化大小为10
         this(10);
     }
     public Vector(int initialCapacity) {
         // 默认扩容因子为0,因此在扩容的时候会增长数组长度的一倍
         this(initialCapacity, 0);
     }
     public Vector(int initialCapacity, int capacityIncrement) {
         // 最终都会调用这个
         super();
         if (initialCapacity < 0)
             throw new IllegalArgumentException("Illegal Capacity: "+
                                                initialCapacity);
         this.elementData = new Object[initialCapacity];
         this.capacityIncrement = capacityIncrement;
     }
 }
  1. 添加元素
  • Add 方法并不会直接暴露给外部调用,会实际修改到数组结构。
  • insertElementAt 方法被synchronized 关键字修饰,同步操作。
    // 添加元素,修改到底层数组结构内容的有如下两个方法:
 // 私有的,供其他 add 方法调用,往s的位置添加
private void add(E e, Object[] elementData, int s) {
     if (s == elementData.length)
         // 超出底层数组大小需要扩容
         elementData = grow();
     elementData[s] = e;
     elementCount = s + 1;
 }
 
 // 插入操作,往index位置插入,同步操作
 public synchronized void insertElementAt(E obj, int index) {
     if (index > elementCount) {
         // 插入范围在0~elementCount
         throw new ArrayIndexOutOfBoundsException(index
                                                  + " > " + elementCount);
     }
     // 父类AbstractList中声明的用来记录结构被修改的次数,可以用于增强for循环中删除数组元素报错的情况。
     modCount++;
     final int s = elementCount;
     Object[] elementData = this.elementData;
     if (s == elementData.length)
         elementData = grow();
     // 将index开始的元素移动到 index+1 后,使用native方法,性能更高
     System.arraycopy(elementData, index,
                      elementData, index + 1,
                      s - index);
     elementData[index] = obj;
     elementCount = s + 1;
 }
  1. 获取元素
 // 获取元素
 public synchronized E get(int index) {
     if (index >= elementCount)
         throw new ArrayIndexOutOfBoundsException(index);
     return elementData(index);
 }
 
 E elementData(int index) {
     return (E) elementData[index];
 }
  1. 删除元素
// 移除元素
 public synchronized E remove(int index) {
     modCount++;
     if (index >= elementCount)
         throw new ArrayIndexOutOfBoundsException(index);
     E oldValue = elementData(index);

     // 要移动元素的数量,如果是index为最后一个元素则不用移除操作
     int numMoved = elementCount - index - 1;
     if (numMoved > 0)
         // 将index+1的位置往前移动即可删除
         System.arraycopy(elementData, index+1, elementData, index,
                          numMoved);
     elementData[--elementCount] = null; // Let gc do its work
     return oldValue;
 }
 
 // 移除所有元素
 public void clear() {
     removeAllElements();
 }
 public synchronized void removeAllElements() {
     final Object[] es = elementData;
     for (int to = elementCount, i = elementCount = 0; i < to; i++)
         es[i] = null;
     modCount++;
 }
  1. 扩容操作
 // 扩容操作
 private Object[] grow() {
     // 至少扩容大于1个元素
     return grow(elementCount + 1);
 }
 private Object[] grow(int minCapacity) {
     int oldCapacity = elementData.length;
     // 获取新的数组长度,当扩容因子为0的时候扩展为原来的1倍大小
     int newCapacity = ArraysSupport.newLength(oldCapacity,
         minCapacity - oldCapacity, capacityIncrement > 0 ? capacityIncrement : oldCapacity);
     return elementData = Arrays.copyOf(elementData, newCapacity);
 }

2.2 ArrayList

底层用数组存储,与Vector相比,ArrayList 的操作不是线程安全的,其他基本与Vector相同。

  1. 类的定义及关键字段
public class ArrayList<E> extends AbstractList<E>
         implements List<E>, RandomAccess, Cloneable, java.io.Serializable
 {
 
     /**
      * 默认初始化数组元素的大小
      */
     private static final int DEFAULT_CAPACITY = 10;
 
     /**
      * 创建没有元素的 ArrayList 的话所有 ArrayList 共享这个对象,节约空间
      */
     private static final Object[] EMPTY_ELEMENTDATA = {};
 
     /**
      * 没有指定初始化容量时使用,可以用于标识在添加第一个元素的时候需要扩容的大小
      */
     private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 
     /**
      * 底层数组结构,通过上面两个全局静态的常量可以发现,当初始化容量为0或者不指定的时候这是一个延迟创建的对象,知道真正添加元素的时候才创建
      */
     transient Object[] elementData; 
 
     /**
      * 元素个数
      */
     private int size;
     
     // 构造方法
     public ArrayList(int initialCapacity) {
         if (initialCapacity > 0) {
             this.elementData = new Object[initialCapacity];
         } else if (initialCapacity == 0) {
             // 容量为0的时候不创建对象
             this.elementData = EMPTY_ELEMENTDATA;
         } else {
             throw new IllegalArgumentException("Illegal Capacity: "+
                                                initialCapacity);
         }
     }
 
     /**
      * 不指定初始化容量的时候在第一次添加元素时会初始化10个元素大小的数组
      */
     public ArrayList() {
         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
     }
 }

其他操作和 Vector 差不多,主要是扩容机制不太一样,ArrayList 没有扩容因子,其在一般情况都是扩容当前长度的 1/2 的大小。
有个特殊的情况就是如果当初始化容量为0的时候第一次添加元素的时候只会扩容一个元素。

 private Object[] grow(int minCapacity) {
     int oldCapacity = elementData.length;
     if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
         int newCapacity = ArraysSupport.newLength(oldCapacity,
             minCapacity - oldCapacity, oldCapacity >> 1);// 右移
         return elementData = Arrays.copyOf(elementData, newCapacity);
     } else {
         // 调用空构造方法时
         return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
     }
}

ArrayList 的扩容机制有点特殊的点在于当没有指定初始容量或者初始容量为0的时候在第一次添加数据的时候就会进入扩容逻辑,是延迟创建对象的:

  • 指定初始化初始容量为0时第一次添加元素创建1个长度的底层数组。
  • 没有指定初始容量时第一次添加元素创建默认长度为10的底层数组。

Vector和ArrayList的异同:

  • 正常情况下底层数组初始大小都为10。
  • Vector 可以指定初始的扩容因子,并且在没有指定的情况下默认扩容一倍大小;而 ArrayList 不能指定扩容因子的大小,正常情况下每次扩容原来的1/2。
  • Vector 的操作都是同步的,使用 synchronized 关键字进行修饰,而 ArrayList 的操作并不是同步的。
  • ArrayList 在初始化容量为0或者没有指定初始化容量的时候做了一些优化,可以实现延迟创建对象的效果。

2.3 LinkedList

底层用双向链表存储数据

  1. 类的定义和关键字段
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() {
     }
     
     // 链表中节点的结构
     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;
         }
     }
 }

从链表中节点的定义可以看出链表是一个双向链表结构。

  1. 添加操作
// 头插
public void addFirst(E e) {
     linkFirst(e);
 }
 private void linkFirst(E e) {
     final Node<E> f = first;
     // 创建节点并连接
     final Node<E> newNode = new Node<>(null, e, f);
     first = newNode;
     if (f == null)
         last = newNode;
     else
         f.prev = newNode;
     size++;
     modCount++;
 }
 
 // 尾插
 public void addLast(E e) {
     linkLast(e);
 }
 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++;
 }
 
 public boolean add(E e) {
     // add默认使用尾插
     linkLast(e);
     return true;
 }
  1. 查询操作
// 查询操作
 Node<E> node(int 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;
     }
 }
  1. 其他
public E peek() {} // 获取第一个元素
public E poll() {} // 删除第一个元素并返回
public E remove() {} // 移除第一个元素
public void push(E e) {}  // 插入第一个元素的位置
public E pop() {} // 弹出第一个元素

总结
在这里插入图片描述

参考:
JDK1.8源码

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值