面试现场 面试官:你好,我是面试官xxx,请问你是大彬吗? 大彬:面试官,您好,我是大彬 面试官:现在方便面试吗? 大彬:嗯嗯,可以的 面试官:那我们现在开始面试吧 面试官:看你简历上写了熟悉集合相关内容,你了解Java的List吗? 大彬:嗯,List是一个接口,常见的实现类有ArrayList和LinkedList 面试官:讲讲这两个实现类的区别? 独白:老八股文了哈哈 大彬:ArrayList的底层数据结构是数组,支持下标访问,查询数据快。默认初始值大小为10,容量不足时会进行扩容 大彬:而LinkedList的底层数据结构是链表,将元素添加到链表的末尾,无需扩容 面试官:嗯嗯,刚刚你提到ArrayList的扩容,详细讲讲? 独白:好家伙,就知道你会问这个,八股文都已经准备好了。 大彬:ArrayList扩容源码实现如下:publicbooleanadd(Ee){ensureCapacityInternal(size1);扩容elementData〔size〕e;returntrue;}privatevoidensureCapacityInternal(intminCapacity){if(elementDataDEFAULTCAPACITYEMPTYELEMENTDATA){minCapacityMath。max(DEFAULTCAPACITY,minCapacity);}ensureExplicitCapacity(minCapacity);}privatevoidensureExplicitCapacity(intminCapacity){modCount;overflowconsciouscodeif(minCapacityelementData。length0)grow(minCapacity);}privatevoidgrow(intminCapacity){overflowconsciouscodeintoldCapacityelementData。length;intnewCapacityoldCapacity(oldCapacity1);if(newCapacityminCapacity0)newCapacityminCapacity;if(newCapacityMAXARRAYSIZE0)newCapacityhugeCapacity(minCapacity);minCapacityisusuallyclosetosize,sothisisawin:elementDataArrays。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容器进行并发的读而不需要加锁,因为当前容器不会被修改。publicbooleanadd(Ee){finalReentrantLocklockthis。lock;lock。lock();add方法需要加锁try{Object〔〕elementsgetArray();intlenelements。length;Object〔〕newElementsArrays。copyOf(elements,len1);复制新数组newElements〔len〕e;setArray(newElements);原容器的引用指向新容器returntrue;}finally{lock。unlock();}} 面试官:那你能说说CopyOnWriteArrayList有什么缺点吗? 大彬:主要有以下两个问题。 大彬:内存占用问题。由于CopyOnWrite的写时复制机制,在进行写操作的时候,内存里会同时驻扎两个对象的内存。 大彬:CopyOnWrite容器不能保证数据的实时一致性,可能读取到旧数据。 面试官:嗯,可以。 面试官:问一些平时开发经常遇到的问题,怎么给List排序呢? 大彬:可以使用list自身的sort方法,或者使用Collections。sort(list)方法。 面试官:怎么在遍历ArrayList时移除一个元素? 大彬:如果使用foreach删除元素的话,会导致快速失败(fastfail)问题,可以使用迭代器的remove()方法,避免fastfail问题。Iteratoritrlist。iterator();while(itr。hasNext()){if(itr。next()。equals(dabin){itr。remove();}} 面试官:基础还不错!回去等通知 大彬:好的,谢谢你 独白:回去等通知?不会凉了吧。。。