乐趣区

关于java:对线面试官-别再问我Java-List八股文了

本期是【对线面试官 】系列文章的第2 期,次要是 Java List 高频面试题目。

回复【面试】获取大彬精心整顿的大厂面试手册,已助力多位小伙伴斩获大厂 offer!

面试现场

面试官:你好,我是面试官 xxx,请问你是大彬吗?

大彬:面试官,您好,我是大彬

面试官:当初不便面试吗?

大彬:嗯嗯,能够的

面试官:那咱们当初开始面试吧

面试官 看你简历上写了相熟汇合相干内容,你理解 Java 的 List 吗?

大彬:嗯,List 是一个接口,常见的实现类有 ArrayList 和 LinkedList

面试官 讲讲这两个实现类的区别?

独白:老八股文了哈哈

大彬:ArrayList 的底层数据结构是数组,反对下标拜访,查问数据快。默认初始值大小为 10,容量有余时会进行扩容

大彬:而 LinkedList 的底层数据结构是链表,将元素增加到链表的开端,无需扩容

面试官 嗯嗯,刚刚你提到 ArrayList 的扩容,具体讲讲?

独白:好家伙,就晓得你会问这个,八股文都曾经筹备好了。

大彬:ArrayList 扩容源码实现如下:

public boolean add(E e) {ensureCapacityInternal(size + 1);  // 扩容
    elementData[size++] = e;
    return true;
}

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;
    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);
}

大彬:能够看到,在 grow 办法外面进行扩容,将数组容量扩充为原来的 1.5 倍。

大彬:举个例子,如果初始化的值是 8,当增加第 9 个元素的时候,发现数组空间不够,就会进行扩容,扩容之后容量为 12。

大彬 :扩容之后,会调用 Arrays.copyOf() 办法对数组进行拷贝。

面试官 嗯,不错,那 ArrayList 和 LinkedList 别离实用于什么场景?

大彬:对于随机 index 拜访的 get 和 set 办法,ArrayList 的速度要优于 LinkedList。因为 ArrayList 间接通过数组下标间接找到元素;LinkedList 要挪动指针遍历每个元素直到找到为止。

大彬:新增和删除元素,LinkedList 的速度要优于 ArrayList。因为 ArrayList 在新增和删除元素时,可能扩容和复制数组;而 LinkedList 的新增和删除操作只须要批改指针即可。

大彬:因而,ArrayList 实用于查问多,增删少的场景。而 LinkedList 实用于查问少,增删多的场景

面试官 讲讲 Set 和 List 的区别?

大彬:List 以索引来存取元素,有序的,元素是容许反复的,能够插入多个 null;Set 不能寄存反复元素,无序的,只容许插入一个 null。

大彬:List 底层实现有数组、链表两种形式;Set 基于 Map 实现,Set 里的元素值就是 Map 的键值。

面试官 理解 Vector 吗?

大彬:嗯,Vector 是底层构造是数组,当初根本没有应用 Vector 了,因为操作 Vector 效率比拟低。绝对于 ArrayList,它是线程平安的,在扩容的时候容量扩大为原来的 2 倍。

面试官 嗯,那你还晓得有哪些线程平安的 List 吗?

大彬 :能够应用 Collections.synchronizedList() 办法返回一个线程平安的 List。

大彬:还有另一种形式,应用 CopyOnWriteArrayList。

面试官 嗯哼,刚刚你提到 CopyOnWriteArrayList,具体讲讲它的原理?

独白:这也太卷了吧,一言不合就是底层原理 …

大彬:CopyOnWriteArrayList 是一个线程平安的 List,底层是通过复制数组的形式来实现的。

大彬:所谓的 CopyOnWrite,就是写时复制。

大彬:当咱们往容器增加元素时,不间接往容器增加,而是先将以后容器进行复制,复制出一个新的容器,而后往新的容器增加元素,增加完元素之后,再将原容器的援用指向新容器。

大彬:这样做的益处就是能够对 CopyOnWrite 容器进行并发的读而不须要加锁,因为以后容器不会被批改。

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock(); //add 办法须要加锁
    try {Object[] elements = getArray();
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1); // 复制新数组
        newElements[len] = e;
        setArray(newElements); // 原容器的援用指向新容器
        return true;
    } finally {lock.unlock();
    }
}

面试官 那你能说说 CopyOnWriteArrayList 有什么毛病吗?

大彬:次要有以下两个问题。

大彬:内存占用问题。因为 CopyOnWrite 的写时复制机制,在进行写操作的时候,内存里会同时驻扎两个对象的内存。

大彬:CopyOnWrite 容器不能保证数据的实时一致性,可能读取到旧数据。

面试官:嗯,能够。

面试官 问一些平时开发常常遇到的问题,怎么给 List 排序呢?

大彬 :能够应用 list 本身的 sort 办法,或者应用 Collections.sort(list) 办法。

面试官 怎么在遍历 ArrayList 时移除一个元素?

大彬:如果应用 foreach 删除元素的话,会导致疾速失败(fast-fail)问题,能够应用迭代器的 remove() 办法,防止 fast-fail 问题。

Iterator itr = list.iterator();
while(itr.hasNext()) {if(itr.next().equals("dabin") {itr.remove();
      }
}

面试官:根底还不错!回去等告诉

大彬:好的,谢谢你

独白:回去等告诉?不会凉了吧 …

退出移动版