当前位置: 首页 > 后端技术 > Java

锁屏面试题100天100刷-java大厂套路作文(day3)

时间:2023-04-01 13:41:15 Java

每天都有地方分享经典面试题,并给出答案,供参考。答案将与问题相关展开,某些问题可能会被抛出以供考虑。这些问题我会标注具体的公司、招聘类型、面试阶段。这些面试题会收录在锁屏面试题app和小程序中。其他面试题每日更新中,感谢您的使用和支持。官网地址:https://www.demosoftware.cc/#/introductionPage,以下是今天的面试题:====ArrayList和LinkedList的实现原理和前者的扩展机制?阿里云【一面】以下内容基于jdk1.8:ArrayList实现原理:Arraylist底层使用一个Object数组elementData来存储数据:transientObject[]elementData;//non-private简化嵌套类访问从创建一个ArrayList开始看,一般通过构造函数创建一个ArrayList看源码:如果(初始容量>0){这个。elementData=newObject[初始容量];}elseif(initialCapacity==0){这个。元素数据=EMPTY_ELEMENTDATA;}else{thrownewIllegalArgumentException("非法容量:"+initialCapacity);}}publicArrayList(Collectionc){elementData=c.toArray();if((size=elementData.length)!=0){//c.toArray可能(错误地)不returnObject[](参见6260652)if(elementData.getClass()!=Object[].class)elementData=Arrays.copyOf(elementData,size,Object[].class);}else{//替换为空数组。this.elementData=EMPTY_ELEMENTDATA;}}很简单,无参构造只会创建一个空数组,一个初始化数组大小的构造函数会创建一个已知容量的ArrayList,再用另一个ArrayList函数初始化一个新的ArrayList再看ArrayList的操作:1)添加:publicbooleanadd(Ee){ensureCapacityInternal(size+1);//递增modCount!!元素数据[大小++]=e;returntrue;}privatevoidensureCapacityInternal(intminCapacity){if(elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA){minCapacity=Math.max(DEFAULT_CAPACITY,minCapacity);}ensureExplicitCapacity(minCapacity);}privatevoidensureExplicitCapacity(intminCapacity){modCount++;//溢出意识代码if(minCapacity-elementData.length>0)grow(minCapacity);}privatevoidgrow(intminCapacity){//溢出意识代码intoldCapacity=elementData.length;intnewCapacity=oldCapacity+(oldCapacity>>1);如果(newCapacity-minCapacity<0)newCapacity=minCapacity;如果(newCapacity-MAX_ARRAY_SIZE>0)newCapacity=hugeCapacity(minCapacity);//minCapacity通常接近于size,所以这是一个胜利:elementData=Arrays.copyOf(elementData,newCapacity);}这里,modCount是一个变量,我们每次改变大小时都会执行自增操作ArrayList,经常被问到的记录修改次数的方法之一就是grow()方法,它是当ArrayList中元素数组中elementData的容量(上面说的存储数据)不够用时的扩容操作。这里我们使用空参构造函数创建了一个ArrayList,第一次向ArrayList中添加元素时,会做一次扩容操作:判断容量是否足够的语句:if(minCapacity-elementData.length>0)在grow中,核心部分是intnewCapacity=oldCapacity+(oldCapacity>>1);新容量是旧容量的1.5倍(oldCapacity>>1实际上是oldCapacity除以2的操作)。参数构造函数第一次添加元素,添加元素时判断数组长度不够。2)删除:publicEremove(intindex){rangeCheck(index);模数++;EoldValue=elementData(index);intnumMoved=大小-索引-1;如果(numMoved>0)System.arraycopy(elementData,index+1,elementData,index,numMoved);元素数据[--大小]=空;//明确让GC完成它的工作returnoldValue;}看代码很简单,就是做一个数组复制,将要删除的某个位置的元素移动到后面的元素,并将旧数组设置为null进行垃圾回收。指定remove(ObjectO)的方法也是先找到对应元素的下标,然后再进行类似上述代码的操作。有兴趣的可以看看源码。查询没什么好说的。底层是数组存储,查询方便。可以自己看源码。LinkedList实现原理:LinkedList存储是通过双向链表实现的:先是transientNode;瞬态节点最后;私有静态类Node{E项;下一个节点;节点上一个;Node(Nodeprev,Eelement,Nodenext){this.item=element;这个.下一个=下一个;this.prev=prev;它的空参数构造什么都不做,带参数的LinkedList(Collectionc)的构造也着重于添加元素的操作,所以直接说它的一些操作。这里需要大家了解链表的增删改查操作,因为LinkedList的这些操作都是基于这些的。希望大家能找资料看懂,这里篇幅有限,就不一一解释了。1)添加:在header中添加:publicvoidaddFirst(Ee){linkFirst(e);}privatevoidlinkFirst(Ee){finalNodef=first;finalNodenewNode=newNode<>(null,e,f);第一个=新节点;如果(f==null)last=newNode;elsef.prev=newNode;尺码++;变量先保存,后保存。这里从头插入和从尾插入基本是一样的操作,只是存储位置不同。看代码就很容易理解了。就是链表的操作。至于add()方法,在末尾插入一个新方法。元素。2)删除removeFirst()、removeLast()和remove()(remove和removeFirst()操作相同),都是简单的删除链表头或尾的操作,可以自行阅读源码,熟悉链表操作的同学很容易看懂,我都能看懂,不再赘述。说说remove(intindex):publicEremove(intindex){checkElementIndex(index);返回取消链接(节点(索引));}Nodenode(intindex){//assertisElementIndex(index);//这里是一个加速搜索的操作//获取索引处的节点。//如果index<双向链表长度的1/2,则从前面查找;//否则,从后往前查找。if(index<(size>>1)){Nodex=first;for(inti=0;ix=最后一个;for(inti=size-1;i>index;i--)x=x.prev;返回x;}}Eunlink(Nodex){//assertx!=null;最终E元素=x.item;最终节点next=x.next;最终节点prev=x.prev;如果(prev==null){first=next;}else{prev.next=next;x.prev=null;}if(next==null){last=prev;}else{next.prev=prev;x.next=null;}x.item=null;尺寸-;模数++;返回元素;}这里是先根据索引找到LinkedList中存储的节点,见上图的node()方法,然后开始删除元素操作,见unlink()方法,熟悉的删除操作链表,很容易理解,判断删除的元素是不是表头和表尾,如果是,简单赋值没错,如果没有就做相应的删除操作Remove(Objecto)。这个方法也是先查找列表中是否有这个元素,先查找一遍,然后调用unlink()方法将其删除。3)SearchLinkedList搜索操作上面的删除操作中,删除步骤(上面的node方法)中已经提到了第一次搜索,请自行对照源码查看搜索操作。