关于 Java foreach 循环删除多个元素的有趣问题(不是抛异常哦)

2018-06-22 22:19:30 +08:00
 shazh520

先看代码:

        import java.util.ArrayList;
	import java.util.List;

	public class ForeachTest {
		public static void main(String[] args) {
			List<Integer> test = new ArrayList<>();
			for (int i = 0; i < 100; i++) {
				test.add(i);
			}
			System.out.println(test);

			for (int i = 0; i < 50; i++) {
				for (int t :
						test) {
					System.out.println(t);
					test.remove(t);
					break;
				}
			}

			System.out.println(test);
		}
	}

完整的代码在上面,各位大佬知道为什么会出现那种运行结果吗?如果知道请指点一二,推荐个阅读资料也行,不胜感激。我 Google 了一晚上只找到了一些关于异常的资料,反编译了以后代码是这样的

	import java.io.PrintStream;
	import java.util.ArrayList;
	import java.util.Iterator;
	import java.util.List;

	public class ForeachTest
	{
		public static void main(String[] paramArrayOfString)
		{
			ArrayList localArrayList = new ArrayList();
			for (int i = 0; i < 100; i++) {
				localArrayList.add(Integer.valueOf(i));
			}
			System.out.println(localArrayList);
			for (i = 0; i < 50; i++)
			{
				Iterator localIterator = localArrayList.iterator();
				if (localIterator.hasNext())
				{
					int j = ((Integer)localIterator.next()).intValue();
					System.out.println(j);
					localArrayList.remove(j);
				}
			}
			System.out.println(localArrayList);
		}
	}

我也看不出端倪啊。 我搞明白了会更贴的。

3491 次点击
所在节点    Java
25 条回复
shazh520
2018-06-22 22:24:45 +08:00
这个格式我也是醉了,调了四五次才勉强可以看。
lhx2008
2018-06-22 22:24:59 +08:00
The iterators returned by this class ’ s iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator ’ s own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

from:
http://jtuts.com/2016/02/26/remove-element-from-alist-during-iteration-in-java/
lhx2008
2018-06-22 22:26:38 +08:00
shazh520
2018-06-22 22:26:47 +08:00
@lhx2008 不是抛异常,是删除元素的位置有点诡异。等我贴运行结果
shazh520
2018-06-22 22:28:41 +08:00
运行结果就像下面这样:

[0, 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]
0
1
2
1
2
...//省略了四十行左右
2
1
2
1
2
[1, 2, 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]
zpxshl
2018-06-22 22:29:03 +08:00
输出结果是什么,我验证下...
shazh520
2018-06-22 22:32:55 +08:00
不对,上面贴那个运行结果是我后面改过代码的结果。
shazh520
2018-06-22 22:34:16 +08:00
运行结果是这样(这个绝对没问题):

[0, 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]
0
1
1
...
1
1
1
[1, 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]
hcymk2
2018-06-22 22:35:39 +08:00
for (Integer t : test) {
System.out.println(t);

test.remove(t);
break;
}
zpxshl
2018-06-22 22:38:03 +08:00
你这代码和反编译代码确定是对应的吗?怎么感觉反编译的代码少了个循环?
lhx2008
2018-06-22 22:38:29 +08:00
@shazh520 蛋疼,你开始代码太乱没看。
你打断点看下就知道了,remove 不是删除那个元素,而是删除那个下标,所以一直在删除 第 1 个 (从 0 开始)元素,然后你 i 会执行 50 次,就是做了 50 次删除第一个元素。唉,还反编译
zpxshl
2018-06-22 22:39:50 +08:00
好吧我错了,少看到个 break...
shazh520
2018-06-22 22:41:07 +08:00
@zpxshl 编译器优化了那个循环,是对应的
lonenol
2018-06-22 22:41:56 +08:00
第一次删了 0,变成 1-99
然后每次都是 remove(1),删第二个元素,保留了 1
就变成你看到的那样了。。
shazh520
2018-06-22 22:45:37 +08:00
@lhx2008 按照这个说法那最后应该是留下了 0 而不是留下 1 没有删除。循环的第一次执行正确的删除了下标为 0 的元素“ 0 ”,但是后面的每次执行为什么就都在删除下标为 1 的元素了呢?
shazh520
2018-06-22 22:47:13 +08:00
@lonenol 我就是搞不明白为啥后面都是删除第二个元素了。 这个太诡异了嘛,没有规律啊。第一次为啥特殊勒,大佬?
lhx2008
2018-06-22 22:48:10 +08:00
@shazh520 第一次是 0123,第一个数就是 0,当然就把 0 删掉了,后面变成 123,第一个数是 1,就一直删第一个数,1 是第 0 个数
zpxshl
2018-06-22 22:48:17 +08:00
@shazh520 你的 list 从 0 开始,第一次循环删掉下标 0,正好值为 0。 list 变成 1 开始。 第二次-n 次循环,每次都是删掉下标为 1 的数,就是删掉 2,3,4,5...
qusthuang
2018-06-22 22:53:14 +08:00
remove(Integer ) 和 remove(int),看下 jdk 实现就知道了
shazh520
2018-06-22 22:53:27 +08:00
@lhx2008 [手动笑哭表情] 大佬,我大体猜到你表达的意思了,你前半句中的第一个数指的是下标 0,后半句中的第一个数指的是下标 1 是吧。 但是我就是想问,为啥第一次删除下标 0,第二次删除下标 1,第三次删除下标 1,...,没规律啊。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/465153

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX