Java容器(一) ArrayList原理及源码分析

容器脑图

Java容器

List 常见的实现类
  • ArrayList

    注:在dubug ArrayList的时候需要main方法中随意打个断点,不要先在内部方法打断点

  //一些常用的变量及方法
  //创建一个默认为空的数组
  private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};    
  //存放元素的数组缓冲区
  transient Object[] elementData;
  //数组的Size
  private int size;
  //默认扩容10
  private static final int DEFAULT_CAPACITY = 10;
  //用来记录数组操作次数
  protected transient int modCount = 0;

  //添加元素
  public boolean add(E e) {
          //(1)ensureCapacityInternal确保容量足够
          ensureCapacityInternal(size + 1);  // Increments modCount!!
          elementData[size++] = e;
          return true;
      }
          //(1)ensureCapacityInternal确保容量足够
  private void ensureCapacityInternal(int minCapacity) {
          //(2)calculateCapacity计算容量 minCapacity最小容量
          //(3)ensureExplicitCapacity确保容量足够
          ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
      }
          //(2)计算返回数组初始化大小 minCapacity最小容量
  private static int calculateCapacity(Object[] elementData, int minCapacity) {
          if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
             return Math.max(DEFAULT_CAPACITY, minCapacity);
          }
          return minCapacity;
      }
          (3)ensureExplicitCapacity确保容量足够
  private void ensureExplicitCapacity(int minCapacity) {
          //记录操作次数
          modCount++;

          // overflow-conscious code
          //(4)grow给数组扩容方法
          if (minCapacity - elementData.length > 0)
              grow(minCapacity);
      }
        //(4)grow给数组扩容方法
  private void grow(int minCapacity) {
          // 获取elementData缓冲区原长度oldCapacity
          int oldCapacity = elementData.length;
          // 创建一个新的缓冲区长度newCapacity  
          // X>>n 的意思是 取 x/2^n   取这个数除以2的n次方
          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);
      }

  public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
      }
      //(5)给数组elementData扩容
  public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
      @SuppressWarnings("unchecked")
              //判断泛型是否为object的子类 返回true则copy等于创建一个容量为10的空数组
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                             Math.min(original.length, newLength));
        return copy;
      }    
      //(5)数组复制(native是调用jvm底层c或c++实现)
   public static native void arraycopy(Object src,  int  srcPos,
                                          Object dest, int destPos,
                                          int length);
  //最后返回到add方法将elementData的size++赋值为E返回True

(1)ensureCapacityInternal 是为了在添加元素的时候确保数组的容量足够

(2)计算返回数组初始化大小 用默认扩容大小DEFAULT_CAPACITY 10 和elementData的size++比较获取 minCapacity最小容量

(3)ensureExplicitCapacity 用(2)方法返回的最小扩容大小和现在的和elementData的size比较确保容量足够

(4)在创建一个空的集合后,虽然默认扩容为10,但是是在第一个元素添加时,用集合的size+1得到最小容量最后取最小容量和默认容量中较大的值来给数组扩容。

(5)具体原理点我查看

总结:
  • ArrayList的底层实现是动态数组,为什么说是动态数组呢?因为元素的添加删除都需要创建一个新的数组去接收最后的结果。
  • ArrayList并没有加锁,所以在多线程操作的时候会产生数据同步问题,所以是线程不安全的。
  • ArrayList是有序可重复的是因为底层使用数组实现的。
2.LinkedList

LinkedList是双向链表实现的,理解其原理更多的是理解链表的的原理

双向列表结构

  //首节点
  transient Node<E> first;
  //尾节点
  transient Node<E> last;
  //内部定义的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;
        }
    }

  //添加元素
  public boolean add(E e) {
         linkLast(e);
         return true;
     }

   //链接元素作为最后一个Node
   void linkLast(E e) {
           //获取记录的最后一个Node
           final Node<E> l = last;
           //新建一个Node 将要添加的元素及刚获取的最后一个Node作为新Node的Prev,Next为空
           final Node<E> newNode = new Node<>(l, e, null);
           //将原先的Last存放的Node替换成现在的Node
           last = newNode;
           //原先最后一个Node为空说明该链表也为空还未添加过元素
           if (l == null)
               //则将第一个设置首Node
               first = newNode;
           else
               //否则设置原先尾Node的Next为现在添加的Node
               l.next = newNode;
           size++;
           modCount++;
       }

   //删除方法
   public E remove(int index) {
        //校验索引是否越界
        checkElementIndex(index);
        return unlink(node(index));
    }
    //获取index位置的节点Node
    Node<E> node(int index) {
         // assert isElementIndex(index);
         //获取链表长度的1/2,判断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;
         }
     }

    //解除要删除的Node与其前后Node的链接
    E unlink(Node<E> x) {
      // assert x != null;
      //获取被删除的值
      final E element = x.item;
      //获取被删除节点的下一个节点
      final Node<E> next = x.next;
      //获取被删除节点的下一个节点
      final Node<E> prev = x.prev;
      //判断上一个节点是否为空,如果为空说明这个被删除的为first节点,则将他的下一个节点设置为first节点,否则将被删除节点的next节点设置到前一个节点上。
      if (prev == null) {
          first = next;
      } else {
          prev.next = next;
          x.prev = null;
      }
      //判断下一个节点是否为空,如果为空说明该删除节点是尾节点,则将尾节点重新设置为被删除节点的前一个节点,否则将删除节点的prev节点设置到nex节点上
      if (next == null) {
          last = prev;
      } else {
          next.prev = prev;
          x.next = null;
      }
      //此处和上面的x.next=null都是为了方便gc的垃圾回收
      x.item = null;
      size--;
      modCount++;
      return element;
  }

总结:
  • LinkedList的增删操作都是对节点的操作。
  • LinkedList也是线程不安全的并没有加同步锁
  • 其余方法就不一一列举,可以自己调试理解
3.Vector

Vector和ArrayList实现方式一样都是使用的动态数组,只是在操作时加了Synchronized来保证多线程操作时的数据安全问题。


其余几种实现类

  • CopyOnWriteArrayList

    顾名思义写时复制,该实现类也保证了 线程安全使用的是ReentrantLock 自旋锁,相比Vector的Synchronized重量级锁来说性能更优。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值