arraylist面试问题,array和arraylist的区别面试题
00-1010问题分析,满腹疑惑,复习全过程,如何正确删除总结。
目录
00-1010小峰听到这个面试问题,心想,这是什么水面试官啊?他怎么能问这么简单的问题?一个for循环加等号判断然后删除不就完了吗?但转念一想,不对,这里面一定有陷阱,不然也不会问这么看似简单的问题。小峰突然想起来以前写代码的时候遇到过这个问题。他还删除了ArrayList中的指定元素,但他在直接循环remove元素时抛出了一个异常。面试官的陷阱估计在这里。小峰暗自得意,发现了面试官布下的陷阱。小峰回忆了一下当天的测试情况,脱敏了一下代码。在开始的时候,需要删除ArrayList中的指定元素。小峰酣畅淋漓地写了下面的代码,自信地点击了运行代码的按钮。结果他很尴尬,抛出了一个异常。
公共类TestListMain { public static void main(String[]args){ ListString result=new ArrayList();result . add( a );result . add( b );result . add( c );result . add( d );for(字符串s :结果){ if (b 。equals(s)){ result . remove( b );}}}}马上就出了一个大大的红色异常,OMG。这是怎么发生的?感觉代码没什么问题。让我们看看抛出了什么异常,它是在哪里抛出的。可以看到抛出了一个ConcurrentmodificationException的异常,是由Itr类中的一个检测方法抛出的。这是怎么回事?我们的原代码里没有这个Itr代码,真的很费解。
00-1010既然从源代码分析看不出来,那我们就来看看源代码编译后类文件的内容。毕竟,类文件是JVM执行的真正代码。不看的话,会很震惊。这就是JDK的打法。原来我们原代码中的for-each语句,实际上是在编译后由迭代器执行的。
public class TestListMain { public TestListMain(){ } public static void main(String[]args){ ListString result=new ArrayList();result . add( a );result . add( b );result . add( c );result . add( d );//Create Iterator Iterator var 2=result . Iterator();while(var 2 . has next()){ String s=(String)var 2 . next();if (b 。equals(s)){ result . remove( b );}}}} Itr,ArrayList创建的内部迭代器,所以for-each循环转化为迭代器加while循环。原来的for-each循环已经当羊头卖了。
公共迭代器iterator(){ return new Itr();}Itr,内部迭代器,通过判断hasNext()判断迭代器是否有内容,而Next()方法获取迭代器中的内容。
私有类Itr实现IteratorE { int cursor//返回int lastRet=-1的下一个元素的索引;//返回的最后一个元素的索引;如果没有这样的int expectedModCount=modCount,则为-1;Itr() {} public boolean hasNext()
{ return cursor != size; } @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } ... }大致的过程如下所示:
真正抛异常的地方是这个检测方法, 当modCount与expectedModCount不相等的时候直接抛出异常了。那我们要看下modCount以及expectedModCount分别是什么。这里的modCount代表ArrayList的修改次数,而expectedModCount代表的是迭代器的修改次数,在创建Itr迭代器的时候,将modCount赋值给了expectedModCount,因此在本例中一开始modCount和expectedModCount都是4(添加了四次String元素)。但是在获取到b元素之后,ArrayList进行了remove操作,因此modCount就累加为5了。因此在进行检查的时候就出现了不一致,最终导致了异常的产生。到此我们找到了抛异常的原因,循环使用迭代器进行循环,但是操作元素却是使用的ArrayList操作,因此迭代器在循环的时候发现元素被修改了所以抛出异常。
我们再来思考下,为什么要有这个检测呢?这个异常到底起到什么作用呢?我们先来开下ConcurrentModificationException的注释是怎么描述的。简单理解就是不允许一个线程在修改集合,另一个线程在集合基础之上进行迭代。一旦检测到了这种情况就会通过fast-fail机制,抛出异常,防止后面的不可知状况。
/** *** * For example, it is not generally permissible for one thread to modify a Collection * while another thread is iterating over it. In general, the results of the * iteration are undefined under these circumstances. Some Iterator * implementations (including those of all the general purpose collection implementations * provided by the JRE) may choose to throw this exception if this behavior is * detected. Iterators that do this are known as <i>fail-fast</i> iterators, * as they fail quickly and cleanly, rather that risking arbitrary, * non-deterministic behavior at an undetermined time in the future. *****/public class ConcurrentModificationException extends RuntimeException { ...}
回顾整个过程
如何正确的删除
既然抛异常的原因是循环使用了迭代器,而删除使用ArrayList导致检测不通过。那么我们就循环使用迭代器,删除也是用迭代器,这样就可以保证一致了。
public class TestListMain { public static void main(String[] args) { List<String> result = new ArrayList<>(); result.add("a"); result.add("b"); result.add("c"); result.add("d"); Iterator<String> iterator = list.iterator(); while (iterator .hasNext()) {String str = iterator.next();if ("b".equals(str)) {iterator.remove();} }}
总结
本文主要对于ArrayList在for循环中进行元素删除出现的异常进行源码分析,这也是面试的时候经常出现的面试陷阱题,面试官通过这样看似简单的题目考察候选者的JDK源码的掌握程度。
真正的大师永远怀着一颗学徒的心
到此这篇关于Java面试必备之ArrayList陷阱解析的文章就介绍到这了,更多相关Java ArrayList内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。