Java编程:删除 List 元素的三种正确方法
2017-08-25 16:07
597 查看
删除 List 中的元素会产生两个问题:
删除元素后 List 的元素数量会发生变化;对 List 进行删除操作可能会产生并发问题;
我们通过代码示例演示正确的删除逻辑
package com.ips.list; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class ArrayListRemove { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("beijing"); list.add("shanghai"); list.add("shanghai"); list.add("guangzhou"); list.add("shenzhen"); list.add("hangzhou"); remove11(list, "shanghai"); } private static void print(List<String> list){ for (String item : list) { System.out.println("元素值:" + item); } } /* * 错误 */ public static void remove11(List<String> list, String target){ int size = list.size(); for(int i = 0; i < size; i++){ String item = list.get(i); if(target.equals(item)){ list.remove(item); } } print(list); } /* * 错误 */ public static void remove12(List<String> list, String target){ for(int i = 0; i < list.size(); i++){ String item = list.get(i); if(target.equals(item)){ list.remove(item); } } print(list); } /* * 错误 */ public static void remove13(List<String> list, String target){ int size = list.size(); for(int i = size - 1; i >= 0; i--){ String item = list.get(i); if(target.equals(item)){ list.remove(item); } } print(list); } /* * 正确 */ public static void remove14(List<String> list, String target){ for(int i = list.size() - 1; i >= 0; i--){ String item = list.get(i); if(target.equals(item)){ list.remove(item); } } print(list); } /* * 错误 */ public static void remove21(List<String> list, String target){ for(String item : list){ if(target.equals(item)){ list.remove(item); } } print(list); } /* * 正确 */ public static void remove22(ArrayList<String> list, String target) { final CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<String>(list); for (String item : cowList) { if (item.equals(target)) { 178d9 cowList.remove(item); } } print(cowList); } /* * 错误 */ public static void remove31(List<String> list, String target){ Iterator<String> iter = list.iterator(); while (iter.hasNext()) { String item = iter.next(); if (item.equals(target)) { list.remove(item); } } print(list); } /* * 正确 */ public static void remove32(List<String> list, String target){ Iterator<String> iter = list.iterator(); while (iter.hasNext()) { String item = iter.next(); if (item.equals(target)) { iter.remove(); } } print(list); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
执行 remove11 方法,出现如下错误:
Exception in thread “main” Java.lang.IndexOutOfBoundsException: Index: 5, Size: 5 at java.util.ArrayList.rangeCheck(ArrayList.java:635)
at java.util.ArrayList.get(ArrayList.java:411)
at com.ips.list.ArrayListRemove.remove11(ArrayListRemove.java:33)
at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)
由于
int size = list.size();提前获取了 List 的大小,for 循环中删除了两个元素,导致出现数组越界问题。
执行 remove12 方法,出现如下错误:
元素值:beijing 元素值:shanghai
元素值:guangzhou
元素值:shenzhen
元素值:hangzhou
字符串“shanghai”没有被删除,该方法解决了数组越界问题,但没有解决彻底删除数据的问题,原因是这样的,跟踪 ArrayList.remove(Object 0) 方法:
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; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
删除元素时执行 else 逻辑,调用了 fastRemove(index) 方法:
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 }1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
通过代码我们发现:List 删除元素的逻辑是将目标元素之后的元素往前移一个索引位置,最后一个元素置为 null,同时 size - 1;这也就解释了为什么第二个“shanghai”没有被删除。
执行 remove13 方法,正确:
元素值:beijing 元素值:guangzhou
元素值:shenzhen
元素值:hangzhou
执行 remove14 方法,正确:
元素值:beijing 元素值:guangzhou
元素值:shenzhen
元素值:hangzhou
那么 remove13 与 remove14 有什么区别呢?答案是没有区别,但是 remove11 与 remove12 是有区别的,remove12 中每次
for(int i = 0; i < list.size(); i++)执行都会计算 size 值,比较耗性能。
执行 remove21 方法,出现如下错误:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859) at java.util.ArrayList$Itr.next(ArrayList.java:831) at com.ips.list.ArrayListRemove.remove21(ArrayListRemove.java:82) at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)1
2
3
4
5
6
1
2
3
4
5
6
产生
java.util.ConcurrentModificationException异常。foreach 写法实际上是对的 Iterable、hasNext、next方法的简写。因此我们从
List.iterator()着手分析,跟踪
iterator()方法,该方法返回了
Itr 迭代器对象。
public Iterator<E> iterator() { return new Itr(); }1
2
3
1
2
3
Itr 类定义代码:
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; 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]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
通过代码我们发现 Itr 是 ArrayList 中定义的一个私有内部类,在 next、remove方法中都会调用 checkForComodification 方法,该方法的作用是判断
modCount != expectedModCount是否相等,如果不相等则抛出
ConcurrentModificationException异常。每次正常执行
remove 方法后,都会对执行
expectedModCount = modCount赋值,保证两个值相等,那么问题基本上已经清晰了,在 foreach 循环中执行
list.remove(item);,对
list 对象的 modCount 值进行了修改,而 list 对象的迭代器的 expectedModCount 值未进行修改,因此抛出了
ConcurrentModificationException异常。
执行 remove22 方法,正确:
元素值:beijing 元素值:guangzhou
元素值:shenzhen
元素值:hangzhou
通过
CopyOnWriteArrayList解决了 List的并发问题。
执行 remove31 方法,出现如下错误:
Exception in thread “main” java.util.ConcurrentModificationException at java.util.ArrayListItr.checkForComodification(ArrayList.java:859)atjava.util.ArrayListItr.next(ArrayList.java:831)
at com.ips.list.ArrayListRemove.remove31(ArrayListRemove.java:109)
at com.ips.list.ArrayListRemove.main(ArrayListRemove.java:17)
与执行 remove21 产生的异常一致,问题产生的原因也一致。
执行 remove32 方法,正确:
元素值:beijing 元素值:guangzhou
元素值:shenzhen
元素值:hangzhou
相关文章推荐
- Java编程:删除 List 元素的三种正确方法
- Java编程:删除 List 元素的三种正确方法
- Java编程:删除 List 元素的三种正确方法
- Java中删除 List 元素的三种正确方法
- Java编程:删除 List 元素的三种正确方法
- 遍历List集合同时删除元素的正确方法
- Java集合之List遍历找到匹配元素删除方法总结
- 正确遍历删除List中的元素方法(推荐)
- JAVA中循环删除list中元素的方法总结
- java中循环遍历删除List和Set集合中元素的方法(推荐)
- 【C#编程】遍历List并删除指定元素的正确方式
- JAVA中循环删除list中元素的方法总结
- JAVA中循环删除list中元素的方法总结
- JAVA中循环删除list中元素的方法总结
- 【Python进阶】删除list里的重复元素的三种方法效率分析
- JAVA中循环删除list中元素的方法总结
- C++ set map list的正确删除元素方法
- java中循环遍历删除List和Set集合中元素的方法
- java中循环遍历删除List和Set集合中元素的方法
- java中循环删除list中元素的方法总结