陷阱:ArrayList遍历时问题
public class Demo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(5);
list.add(5);
list.add(6);
list.add(7);
int removeValue = 5;
for (int i = 0; i < list.size(); i ++) {
if ( list.get(i) == removeValue ) {
list.remove(i);
}
}
for (int i = 0; i < list.size(); i ++) {
System.out.println(list.get(i));
}
}
}
告诉我你认为的答案:
如果你原来没注意过这样的问题那么你一定会出错:
分析:
你一定在想为什么:
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
这里是remove的源码
这里看不到我们想要的跟到fastRemove()
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
size肯定是list的有效元素长度
index是当前数组下标
但是fastRemove()一次后,index并没有回退,而是继续执行,所以结果你就知道了把
解决
我只是点一下,为什么这样做自己想
1. index –;
2. 向后遍历删除
3. 迭代器Iterator删除
4.
public E next() {
5. checkForComodification();
6. int i = cursor;
7. if (i >= size)
8. throw new NoSuchElementException();
9. Object[] elementData = ArrayList.this.elementData;
10. if (i >= elementData.length)
11. throw new ConcurrentModificationException();
12. cursor = i + 1;
13. return (E) elementData[lastRet = i];
14. }
-
- 16.
public void remove() {
17. if (lastRet < 0)
18. throw new IllegalStateException();
19. checkForComodification();
20.
21. try {
22. ArrayList.this.remove(lastRet);
23. cursor = lastRet;
24. lastRet = -1;
25. expectedModCount = modCount;
26. } catch (IndexOutOfBoundsException ex) {
27. throw new ConcurrentModificationException();
28. }
29. }
Iterator里通过next()获取数据, i 执行当前cursor时,当前cursor + 1, 返回element[i]也是就当前的数据,lastRet则记录当前操作完的下标i。
因为我们通过迭代器获取数据时首先通过hasNext()看是否有数据,再通过next()得到当前数据,如果这进行remove(lastRet)说明删除了当前元素(lastRet在next()时指向了当前的索引),而remove中的cursor又重新指向lastRet相当于做了一次回退操作
扩展下
== 与 equals
public class Demo2 {
public static void main(String[] args) {
Integer a = 12;
Integer b = 12;
System.out.println(a == b);
a = 9999;
b = 9999;
System.out.println(a == b);
}
}
答案是: true false
为什么
源码:
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
对1 个字节长度(-128 ~ 127 )进入了一次缓存
缓存里的直接引用缓存地址,所以第一为true,不在缓存里的new一个对象出来,所以2为false
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
equals就更简单了,匹配类型,再匹配内容
如果扯远了就有String,StringBuffer,StringBuild, String 的 == 与equals, intern。还有Java对象值传递还是对象传递问题了。
多花点时间看看源码就都OK了。