面试——ArrayList

导读:本篇文章讲解 面试——ArrayList,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

面试——ArrayList

1. 底层研究

1. ArrayList

底层是数组

1. 无参构造

构造一个初始容量为10的空列表
在这里插入图片描述
当给空数组链表添加第一个数据的时候,数组链表才会初始化长度
在这里插入图片描述
在这里插入图片描述

2. 有初始化容量的构造

在这里插入图片描述

3. 扩容

在这里插入图片描述

2. LinkedList

底层是链表

2. 有用过ArrayList吗?它是做什么用的?

  1. ArrayList就是数组列表,底层是数组
  2. ArrayList在装载基本数据类型时,实际装载的是对应的包装类
  3. 与ArrayList类似的还有LinkedList,他们俩相比:
  • ArrayList:查找和访问元素的速度快,新增、删除的速度慢。线程不安全,使用频率高。查询时间复杂度O(1)
  • LinkedList:查找和访问元素的速度慢,新增、删除的速度快。查询时间复杂度O(n)
    在这里插入图片描述

3. ArrayList线程不安全,为什么还要去用?

Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢。

实际开发中有以下几种场景:

  • 频繁增删:使用LinkedList,但是涉及到频繁增删的场景不多
  • 追求线程安全:使用Vector
  • 普通的用来查询:使用ArrayList,使用的场景最多

根据数据结构的特性,三者难以全包含,只能在相互之间做取舍

4. ArrayList底层是数组,那是怎么实现不断扩容的?

  1. 使用ArrayList空参的构造器创建集合时,数组并没有创建。当集合中添加第一个元素时,数组被创建,初始化容量为10
  2. 当数组长度+1>数组容量时,这时需要对数组进行扩容,扩容公式:新长度=原长度+原长度>>1,也就是扩容到了原来的1.5倍
 /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     */
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //新容量=旧容量+1/2旧容量
        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);
    }

5. ArrayList(int initialCapacity)会不会初始化数组大小?

会初始化大小,但如果通过ArrayList的size()方法进行判断时结果依然为0,因为只有在添加元素时才会对ArrayList的size属性+1

6. ArrayList1.7之前和1.7及以后的区别?

  1. 1.7之前ArrayList在初始化的时候直接调用this(10),初始化容量为10的数组。
  2. 在1.7及以后,只有第一次执行add方法向集合添加元素时才会创建容量为10的数组。

7. 为什么ArrayList增删比较慢,增删是如何做的?

  1. 如果没有指定index,添加元素之间追加到数组最后,若容量不够,则需要扩容
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
  1. 若指定了index元素

从源码里看到,是将要添加的元素位置index之后的已有元素全部拷贝到添加到原数组index+1处,然后再把新的数据加入。

public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

8. ArrayList插入和删除数据一定慢吗?

不一定,取决于删除的数据离数组末端有多远,如果离末端较近,则性能不一定差。

9. ArrayList如何删除数据?

ArrayList删除数据时同样使用拷贝数组的方式,将要删除的位置之后的所有元素拷到当前位置,最后再对最后一个位置的数据设置为null,交给gc来回收。这种删除,其实就是覆盖,如果数据量大,那么效率很低。

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
    }

10. ArrayList适合做队列吗?

  1. 队列需要遵循先进先出的原则
  2. 如果从ArrayList的数组头部入队列,数组尾部出队列,那么对于入队列时的操作,会涉及大数据量的数组拷贝,十分耗性能。
  3. 队头队尾反一反也是一样,因此ArrayList不适合做队列。

11. 数组适合做队列吗?

  1. ArrayBlockingQueue环形队列就是用数组来实现的
  2. ArrayBlockingQueue的存和取操作的索引是在当索引值等于容量值时,将索引值设置为0实现环形队列的效果,因此在这种情况下,数组适合做队列
 /**
     * Inserts element at current put position, advances, and signals.
     * Call only when holding lock.
     */
    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

    /**
     * Extracts element at current take position, advances, and signals.
     * Call only when holding lock.
     */
    private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

在这里插入图片描述

12. ArrayList和LinkedList两者的遍历性能孰优孰劣?

  1. ArrayList的遍历性能明显要比LinkedList好
  2. 因为ArrayList存储的数据在内存中时连续的,CPU内部缓存结构会缓存连续的内存片段,可以大幅降低读取内存的性能开销。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/65632.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!