暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java面试一天一题(day 1面试题:ArrayList中的remove是如何操作的?)

架构狂人 2021-06-29
1874

面试题:ArrayList中的remove是如何操作的?

我接到面试电话的一刻,以为是骚扰电话打来的,一看显示四川乐山,哦,原来是我投的成都蚂蚁的面试,说简单聊聊吧,上来问了个ArraList热了下身。

 ArrayList是个变长的数组集合类,实现是通过Object[],当向ArrayList添加元素数量大于内部的数组容量时,会进行自动扩容1.5倍,新增和删除我们可以通过下标,指定位置新增和删除,如果是在有值的位置插入和删除数据,则需要移动数据保证数组连续。

面试官:嗯,那你谈谈ArrayListdd的扩容机制吧。

谈扩容机制前,我们需要对ArrayList的数据结构有个大致了解,下面会结合图片讲述。

构造方法
    包含空参构造和带容量构造;
    重要属性:初始容量10,当前数组长度
    //初始容量:10
    private static final int DEFAULT_CAPACITY = 10;


    // 空对象,如果使用默认构造函数创建,则默认对象内容默认是该值
    private static final Object[] EMPTY_ELEMENTDATA = {};


    //无参初始化并不是在无参构造方法的位置执行的,而是在第一次执行add方法的时候执行了容器大小的设置
    //简单说,new ArrayList();容器初始化大小为0.
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};


    //当前数据对象存放地方,当前对象不参与序列化
    transient Object[] elementData;


    //当前数组长度
    private int size;


    public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
    this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
    this.elementData = EMPTY_ELEMENTDATA;
    } else {
    throw new IllegalArgumentException("Illegal Capacity: "+
    initialCapacity);
    }
    }
    public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }  
        
    add方法

    不带下标插入,插入队尾
    ②带下标插入,指定位置插入

    1. 队尾插入

        两个步骤:
        (1)判断是否需要扩容
        (2)插入新元素到队尾

      public boolean add(E e) {
      // 1. 判断是否需要扩容
      ensureCapacityInternal(size + 1); // Increments modCount!!
      // 2. 将新元素插入数组尾部
      elementData[size++] = e;
              return true;
       }

      2. 指定位置插入

          三个步骤:
          (1)判断是否需要扩容
          (2)数组拷贝,移位
          (3)插入指定位置,index
       
        public void add(int index, E element) {
        if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));


        // 1. 判断是否需要扩容
        ensureCapacityInternal(size + 1); // Increments modCount!!
            // 2. 将 index及其之后的所有元素都向后移一位
        // arraycopy(被复制的数组, 从第几个元素开始, 复制到哪里, 从第几个元素开始粘贴, 复制的元素个数)
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        // 3. 将新元素插入至 index 处
        elementData[index] = element;
        size++;
        }

        扩容机制

        当空间用完,会调用grow方法将,原数组空间扩容为原来的1.5倍
            
          // 判断是否需要扩容
          private void ensureCapacityInternal(int minCapacity) {
          if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
          minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
              }
          ensureExplicitCapacity(minCapacity);
          }


          private void ensureExplicitCapacity(int minCapacity) {
          modCount++;
          // overflow-conscious code
          if (minCapacity - elementData.length > 0)
          grow(minCapacity);
          }


          //最终执行扩容方法
          private void grow(int minCapacity) {
          // overflow-conscious code
          int oldCapacity = elementData.length;
          // newCapacity = oldCapacity + oldCapacity 2 = oldCapacity * 1.5
          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 static int hugeCapacity(int minCapacity) {
          if (minCapacity < 0) // overflow
          throw new OutOfMemoryError();
          // 如果最小容量超过 MAX_ARRAY_SIZE,则扩容至 Integer.MAX_VALUE
          return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
          }  


          remove方法

          1. 获取指定位置 index 处的元素值
          2. 将 index + 1 及之后的元素向前移动一位
          3. 将最后一个元素置空,并将 size 值减 1
          4. 返回被删除值,完成删除操作

             
             public E remove(int index) {
            if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
            modCount++;
            // 返回被删除的元素值
            E oldValue = (E) elementData[index];


            int numMoved = size - index - 1;
            if (numMoved > 0)
            // 将 index + 1 并将之后的元素向前移动一位,覆盖被删除值
            System.arraycopy(elementData, index+1, elementData, index,
            numMoved);
            // 将最后一个元素置空,并将size值减 1
            elementData[--size] = null; // clear to let GC do its work
            return oldValue;
            }
            E elementData(int index) {
            return (E) elementData[index];
            }


            //删除指定元素,若元素重复,则只删除下标最小的元素
            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;
            }


            /** 快速删除,不做边界检查,也不返回删除的元素值 */
            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
            }



            文章转载自架构狂人,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

            评论