List集合底层解析

Zeran

解析List集合子类ArrayList、Vector、LinkedList底层及扩容机制

ArrayList

特点

  • 高效率
  • 数组
  • 查询和修改快
  • 不安全
  • 有序

    扩容机制

    1.使用无参构造器初始化时,会创建一个elementData的空数组,没有初始容量.

      /**
       * DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为一个空数组
       * private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
       * Constructs an empty list with an initial capacity of ten.
       */
        public ArrayList() {
          this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
      }

    2.当调用集合第一次进行add操作时,会进行当前数组elementData是否是空判断,如果为空,则形成一个最小所需容量为10的最小长度,随后则进行扩容,如果根据所需容量计算的结果小于0,则进行新容量的反赋值操作,随后则调用Arrays.copyOf方法进行数组的赋值,并且完成赋值。
    3.当所需容量大于10的时候,则按照现有容量的1.5倍进行扩容,并完成赋值。
    4.使用有参构造时则使用有参构造初始化elementData数组,当容量不够时,同上3

     private void grow(int minCapacity) {
          // overflow-conscious code
          // 当前数组的长度
          int oldCapacity = elementData.length;
          // 计算扩容
          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);
      }

    Vector集合

    特点

  • 低效率
  • 数组
  • 安全(synchronized悲观锁)
  • 有序

    扩容机制

    1.使用无参构造初始化时,默认容量为10.

    /**
       * Constructs an empty vector so that its internal data array
       * has size {@code 10} and its standard capacity increment is
       * zero.
       */
      public Vector() {
          this(10);
      }

    2.当所需容量小于现有容量时,会按照现有容量的2倍进行扩容

    private void grow(int minCapacity) {
          // overflow-conscious code
          int oldCapacity = elementData.length;
          // 因为capacityIncrement默认为0,所以扩容等同于:现有容量+现有容量(2倍)
          int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                           capacityIncrement : oldCapacity);
          if (newCapacity - minCapacity < 0)
              newCapacity = minCapacity;
          if (newCapacity - MAX_ARRAY_SIZE > 0)
              newCapacity = hugeCapacity(minCapacity);
          elementData = Arrays.copyOf(elementData, newCapacity);
      }

    3.调用Arrays.copyOf方法进行数组的复制,并完成赋值。

    LinkedList

    特点

  • 增加和删除快
  • 底层非数组,而是一个Node对象[prev(上一个元素)、item(存储)、next(下一个元素))]
  • 不安全
  • 双向链表

    解析

    1.首先要知道linkedList的一个内部类Node对象,这个对象有三个属性,分别是item:要存储的值,next:下一个元素,prev:上一个元素。

    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.1可以自己创建一个Node节点对象,玩一下node节点的如何连接。

    class Node<T> {
      public T obj;
    
      public Node<T> next;
    
      public Node<T> pre;
    
      public Node(T obj) {
          this.obj = obj;
      }
    
      @Override
      public String toString() {
          return "Node{" +
                  "obj=" + obj +
                  '}';
      }
    }

    1.2测试代码,创建三个node节点,将每个节点相连接,形成一种链式结构。

          Node<String> XM = new Node<>("小明");
          Node<String> ZS = new Node<>("张三");
          Node<String> LS = new Node<>("李四");
    
          // 让每个node节点的下一个节点指向下一个对象
          XM.next = ZS;
          ZS.next = LS;
    
          // 每个节点的上一个指向
          LS.pre = ZS;
          ZS.pre = XM;
    
          Node<String> first = new Node<>("First");
          Node<String> last = new Node<>("Last");
          first = XM;
          // 根据首元素遍历
          while (true) {
              if (first == null) {
                  break;
              }
              System.out.println(first);
              first = first.next;
          }
          System.out.println("==================pre=================");
          last = LS;
          while (true) {
              if (last == null) {
                  break;
              }
              System.out.println(last);
              last = last.pre;
          }
    
          // 讲小红插入张三和李四之间
          /**
           * 1.将张三的下一个元素指向小红
           * 2.将小红的下一个元素指向李四
           * 3.将小红的上一个元素指向张三
           * 4.将李四的上一个元素指向小红
           */
          Node<String> XH = new Node<>("小红");
          ZS.next = XH;
          XH.next = LS;
          XH.pre = ZS;
          LS.pre = XH;
          System.out.println("==========小红插入后=============");
          first = XM;
          // 根据首元素遍历
          while (true) {
              if (first == null) {
                  break;
              }
              System.out.println(first);
              first = first.next;
          }
          System.out.println("==================pre=================");
          last = LS;
          while (true) {
              if (last == null) {
                  break;
              }
              System.out.println(last);
              last = last.pre;
          }

    2.linkedList集合初始化后会创建一个空集合
    此时内部的三个属性,size为0,first和last属性为null。

    /**
       * Constructs an empty list.
       */
      public LinkedList() {
      }

    3.当集合进行第一次add操作时,真正做的是将最后一个元素和新添加的元素相连接,此时集合的last属性为null,则元素e既是first又是last元素。

    /**
       * Links e as last element.
       */
      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++;
      }

    4.当集合第二次进行add操作时,会直接将last属性作为新元素的上一个元素,此时新元素则为集合的last元素,此时的l不为空,则将l的下一个元素指向新元素。

阅读 240

学而不思则罔,思而不学则殆。

1 声望
2 粉丝
0 条评论
你知道吗?

学而不思则罔,思而不学则殆。

1 声望
2 粉丝
宣传栏