写点什么

解析字节算法面试真题,深入探究 ArrayList 应用原理

用户头像
小Q
关注
发布于: 2020 年 12 月 16 日

ArrayList是Java的链表类,作为Java三大容器组成之一的List的组成部分,如下图所示





在项目开发中常用,虽然他有一些不足的地方,但是这不足以替代他帮助程序员解决大量问题的光辉,正是由于他被经常使用,所以在面试的时候也被经常问到,比方说字节、腾讯这一类对于算法实现以及源码考察比较重视的公司更是如此,今天我就通过几道面试题,以面试的身份对于ArrayList进行讲解



关注公众号:Java架构师联盟,每日更新技术好文



题目:【java源码】ArrayList



ArrayList 常用功能:构造函数、增、批量增、删、批量删、批量保留



ArrayList 属性:



// 默认数组长度(数组,而不是数据个数)



private static final int DEFAULT_CAPACITY = 10;



// 空数据



private static final Object[] EMPTY_ELEMENTDATA = {};



private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};



// 实际用于存放数据的地方



transient Object[] elementData;



// 数据个数



private int size;

ArrayList 常用功能:构造函数、增、批量增、删、批量删、批量保留ArrayList 属性:



  // 默认数组长度(数组,而不是数据个数)



  private static final int DEFAULT_CAPACITY = 10;



  // 空数据



  private static final Object[] EMPTY_ELEMENTDATA = {};



  private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};



  // 实际用于存放数据的地方



  transient Object[] elementData;



  // 数据个数



  private int size;

1、构造函数



  ①public ArrayList();



      只干了一件事:this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;



  ②public ArrayList(int initialCapacity);



      指定初始化elementData数组的初始大小。this.elementData = new Object[initialCapacity];



  ③public ArrayList(Collection<? extends E> c) {



      elementData = c.toArray();



      if ((size = elementData.length) != 0) {



          // c.toArray might (incorrectly) not return Object[] (see 6260652)



          if (elementData.getClass() != Object[].class)



              elementData = Arrays.copyOf(elementData, size, Object[].class);



      } else {



          // replace with empty array.



          this.elementData = EMPTY_ELEMENTDATA;



      }



  }



   

2、增:就是数组中插一个元素操作思路



  ①public boolean add(E e);



      1、确保elementData数组能够装下



          首先判断原来数组长度是否为0,如果为零,那么新数组长度为Math.max(DEFAULT_CAPACITY, 加入后数组最小应该长度);



          判断原来数组是否还装的下,如果需要扩容那么:



          private void grow(int minCapacity) { // 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);



          }

2、elementData[size++] = e;return true;



  ②public void add(int index, E element);



      1、rangeCheckForAdd(index); // 检测index是否越接 if (index > size || index < 0)



      2、确保elementData数组能够装下



      3、System.arraycopy(elementData, index, elementData, index + 1, size - index); // 数组挪位



      4、elementData[index] = element;



      5、size++;

3、批量增:就是数组中插多个元素操作思路



  ①public boolean addAll(Collection<? extends E> c);



      public boolean addAll(Collection<? extends E> c) {



          Object[] a = c.toArray();



          int numNew = a.length;



          ensureCapacityInternal(size + numNew); // 同上:确保elementData数组能够装下



          System.arraycopy(a, 0, elementData, size, numNew);



          size += numNew;



          return numNew != 0;



      }



  ②public boolean addAll(int index, Collection<? extends E> c)



      public boolean addAll(int index, Collection<? extends E> c) {



          rangeCheckForAdd(index);





          Object[] a = c.toArray();



          int numNew = a.length;



          ensureCapacityInternal(size + numNew); // // 同上:确保elementData数组能够装下





          int numMoved = size - index;



          if (numMoved > 0)



              System.arraycopy(elementData, index, elementData, index + numNew,



                                numMoved);





          System.arraycopy(a, 0, elementData, index, numNew);



          size += numNew;



          return numNew != 0;



      }

4、删:三点注意①按内容删,只删除第一个;②删的是equals为真的;③注意看fastRemove(index)源码



  ①public boolean remove(Object o);



      public boolean remove(Object o) {



          if (o == null) {



              for (int index = 0; index < size; index++)



                  if (elementData[index] == null) {



                      fastRemove(index);



                      return true;



                  }



          } else {



              for (int index = 0; index < size; index++)



                  if (o.equals(elementData[index])) {



                      fastRemove(index);



                      return true;



                  }



          }



          return false;



      }



  ②public E remove(int index); // 同理



  ③private void fastRemove(int index);



      private void fastRemove(int index) {



          modCount++;



          int numMoved = size - index - 1;



          if (numMoved > 0)



              System.arraycopy(elementData, index+1, elementData, index,



                                numMoved);



          elementData[--size] = null; // clear to let GC do its work



      }

5、批量删、批量保留:一点注意:batchRemove方法。



  ①public boolean removeAll(Collection<?> c);



      public boolean removeAll(Collection<?> c) {



          Objects.requireNonNull(c);



          return batchRemove(c, false);



      }



  ②public boolean retainAll(Collection<?> c);



      public boolean retainAll(Collection<?> c) {



          Objects.requireNonNull(c);



          return batchRemove(c, true);



      }



  ③private boolean batchRemove(Collection<?> c, boolean complement);



      // 思想:维护两个指针。一个读指针(r),一个写指针(w)。读指针从0遍历到数组尾,在遍历中,如果符合条件就elementData[w++] = elementData[r];



      private boolean batchRemove(Collection<?> c, boolean complement) {



          final Object[] elementData = this.elementData;



          int r = 0, w = 0;



          boolean modified = false;



          try {



              for (; r < size; r++)



                  if (c.contains(elementData[r]) == complement)



                      elementData[w++] = elementData[r];



          } finally {



              // Preserve behavioral compatibility with AbstractCollection,



              // even if c.contains() throws.



              if (r != size) {



                  System.arraycopy(elementData, r,



                                    elementData, w,



                                    size - r);



                  w += size - r;



              }



              if (w != size) {



                  // clear to let GC do its work



                  for (int i = w; i < size; i++)



                      elementData[i] = null;



                  modCount += size - w;



                  size = w;



                  modified = true;



              }



          }



          return modified;



      }



     

总结:



1、ArrayList通过内置一个Object数组实现顺序表功能。通过grow函数实现动态增长,最大长度可在源码中找答案。2、删除功能通过fastRemove函数实现对一个删除,通过batchRemove函数实现对多个删除。通过elementData[i] = null;或elementData[--size] = null; 的方式,让JVM垃圾回收,自动回收。



  private void grow(int minCapacity) { // 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);



  }



       



  private void fastRemove(int index) {



      modCount++;



      int numMoved = size - index - 1;



      if (numMoved > 0)



          System.arraycopy(elementData, index+1, elementData, index,



                            numMoved);



      elementData[--size] = null; // clear to let GC do its work



  }



       



  private boolean batchRemove(Collection<?> c, boolean complement) {



      final Object[] elementData = this.elementData;



      int r = 0, w = 0;



      boolean modified = false;



      try {



          for (; r < size; r++)



              if (c.contains(elementData[r]) == complement)



                  elementData[w++] = elementData[r];



      } finally {



          // Preserve behavioral compatibility with AbstractCollection,



          // even if c.contains() throws.



          if (r != size) {



              System.arraycopy(elementData, r,



                                elementData, w,



                                size - r);



              w += size - r;



          }



          if (w != size) {



              // clear to let GC do its work



              for (int i = w; i < size; i++)



                  elementData[i] = null;



              modCount += size - w;



              size = w;



              modified = true;



          }



      }



      return modified;



  }



发布于: 2020 年 12 月 16 日阅读数: 15
用户头像

小Q

关注

还未添加个人签名 2020.06.30 加入

小Q 公众号:Java架构师联盟 作者多年从事一线互联网Java开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果能为您提供帮助,请给予支持(关注、点赞、分享)!

评论

发布
暂无评论
解析字节算法面试真题,深入探究ArrayList应用原理