ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 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);     }      // 还有好几个重载的方法        } ~~~ &nbsp; ***** 下面是List的常见实现类: ### Vector 底层用数组存储,里面的方法都是线程安全的,现版本基本别抛弃了,不建议使用。 ~~~  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;     }            // -------------------------------------------      // 3. 常见操作      // 添加元素,修改到底层数组结构内容的有如下两个方法:      // 私有的,供其他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;     }            // 获取元素      public synchronized E get(int index) {          if (index >= elementCount)              throw new ArrayIndexOutOfBoundsException(index);  ​          return elementData(index);     }      E elementData(int index) {          return (E) elementData[index];     }            // 移除元素      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++;     }         // --------------------------------------      // 扩容操作      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);     }  } ~~~ &nbsp; ***** ### ArrayList 底层用数组存储,与Vector相比,ArrayList的操作不是线程安全的,其他基本与Vector相同。 ~~~  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的时候在第一次添加数据的时候就会进入扩容逻辑,是延迟创建对象的: 1. 初始容量为0时第一次添加元素创建1个长度的底层数组。 2. 没有指定初始容量时第一次添加元素创建默认长度为10的底层数组。 3. 初始化容量指定为0时,第一次添加元素的数组长度会扩容为1。 Vector和ArrayList的异同: 1. 正常情况下底层数组初始大小都为10。 2. Vector可以指定初始的扩容因子,并且在没有指定的情况下默认扩容一倍大小;而ArrayList不能指定扩容因子的大小,正常情况下每次扩容原来的1/2。 3. Vector的操作都是同步的,使用synchronized关键字进行修饰,而ArrayList的操作并不是同步的。 4. ArrayList在初始化容量为0或者没有指定初始化容量的时候做了一些优化,可以实现延迟创建对象的效果。 &nbsp; ***** ### LinkedList 底层用双向链表存储数据 ~~~  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;         }     }            // ------------------------------------------      // 常见操作      // 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;     }                  // 查询操作      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;         }     }            // 算了...其他就需要再看吧,下面这些都是队列的相关操作,注意其实现了Dequeu      public E peek() {} // 获取第一个元素      public E poll() {} // 删除第一个元素并返回      public E remove() {} // 移除第一个元素      public void push(E e) {}  // 插入第一个元素的位置      public E pop() {} // 弹出第一个元素  } ~~~ 从源代码可以看出LinkedList是一个双端链表,不仅可以作为链表使用,还可以作为队列使用。 &nbsp; 参考: JDK1.8源码