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
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
哈哈 这个是我的面试题,确实好多人载这上面了