Java集合类ArrayList删除特定元素
前一段时间入职新公司,熟悉公司系统原有代码的时候,发现公司代码那个烂啊,系统能正常跑,都不能用侥幸来形容,就是创造了一个奇迹。因为里面不仅没有coding style,而且竟然有很明显的常识性错误。其中当我一眼指出最明显的早就应该出过问题的一个地方,项目组几乎所有成员,是的,几乎全部成员,都说这个还真不知道,涨知识了,那就是:Java集合类ArrayList删除特定元素。发现原来不是所有人都知道怎么做,这难道不是最基础的吗?唉,真不知道这些系统是怎么跑起来的。我们首先看两种错误的写法,第一种:
@Test public void testRemove1() { List<String> list = new ArrayList<String>() { private static final long serialVersionUID = 1L; { add("cn"); add("bridgeli"); add("blog"); } }; for (int i = 0, len = list.size(); i < len; i++) { String str = list.get(i); if ("cn".equals(str)) { list.remove(str); } } }
这个写法如果你不知道错在哪,那你得真的好好补基础了。由于这个错误比较明显,所以有人搞了下面这种写法,也是我们公司的同事犯的一个错误:
@Test public void testRemove2() { List<String> list = new ArrayList<String>() { private static final long serialVersionUID = 1L; { add("cn"); add("bridgeli"); add("blog"); } }; for (String str : list) { if ("cn".equals(str)) { list.remove(str); } } }
跑一下这个例子看看,把cn换成bridgeli试试,出乎不出乎你的意料?下面我们就来简单探究一下原因foreach的原理,其实特别简单:
Java.util.List实现了java.lang.Iterable接口。
jdk api文档中是这样描述Iterable接口的:实现这个接口允许对象成为 “foreach” 语句的目标。看一下Iterable接口并没有发现啥特别之处,只是定义了一个迭代器而已。用javap看一下字节码,你就会发现:foreach语法最终被编译器转为了对Iterator.next()的调用。而作为使用者的我们,jdk并没用向我们暴露这些细节,我们甚至不需要知道Iterator的存在,怎么样编译器厉害吧?如果你还不相信,你可以用Iterator写了个遍历List的方法看一下字节码看看和foreach是不是一样。既然是调用了Iterator.next(),那这个方法有什么特殊吗?看看实现就知道了。
@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]; }
方法的第一行调用了checkForComodification(),而他的实现呢?
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
到这应该很明显了吧,看list的remove()实现,每次remove()的时候modCount都会++,而expectedModCount只会在初始化的时候赋过一次值。然后他们的值就不一样了,然后你肯定知道了。看完了错误的写法,我们看看其他的最正确的写法:
@Test public void testRemove3() { List<String> list = new ArrayList<String>() { private static final long serialVersionUID = 1L; { add("cn"); add("bridgeli"); add("blog"); } }; Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String str = iterator.next(); if ("blog".equals(str)) { iterator.remove(); } } }
这个为什么不会出错,我相信根据刚才的分析,大家一定可以找到答案。还有为什么说这个最正确呢?因为还有其他的写法,我个人认为那些写法实在太low就不展示了,所以大家今后有这种需求就用这种写法就好了。最后的最后推荐大家看一看阿里巴巴新出的Java开发手册,我相信这些肯定是阿里的同行踩过的坑的总结,里面不仅对coding style做了严格要求,而且还能避免很多常识性错误,而且有些还说明了原因,里面就有对这个问题的说明,所以看一下吧,虽然里面的部分观点你可能不认同,但我相信看完实践一下你会受益良多。
作 者: BridgeLi,https://www.bridgeli.cn
原文链接:http://www.bridgeli.cn/archives/363
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
哈哈 这个是我的面试题,确实好多人载这上面了