首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
shazh520
V2EX  ›  Java

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

  •  2
     
  •   shazh520 · 2018-06-22 22:19:30 +08:00 · 2316 次点击
    这是一个创建于 747 天前的主题,其中的信息可能已经有所发展或是发生改变。

    先看代码:

            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);
    		}
    	}
    

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

    25 条回复    2018-06-24 12:50:45 +08:00
    shazh520
        1
    shazh520   2018-06-22 22:24:45 +08:00
    这个格式我也是醉了,调了四五次才勉强可以看。
    lhx2008
        2
    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/
    shazh520
        4
    shazh520   2018-06-22 22:26:47 +08:00
    @lhx2008 不是抛异常,是删除元素的位置有点诡异。等我贴运行结果
    shazh520
        5
    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
        6
    zpxshl   2018-06-22 22:29:03 +08:00 via Android
    输出结果是什么,我验证下...
    shazh520
        7
    shazh520   2018-06-22 22:32:55 +08:00
    不对,上面贴那个运行结果是我后面改过代码的结果。
    shazh520
        8
    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
        9
    hcymk2   2018-06-22 22:35:39 +08:00
    for (Integer t : test) {
    System.out.println(t);

    test.remove(t);
    break;
    }
    zpxshl
        10
    zpxshl   2018-06-22 22:38:03 +08:00 via Android
    你这代码和反编译代码确定是对应的吗?怎么感觉反编译的代码少了个循环?
    lhx2008
        11
    lhx2008   2018-06-22 22:38:29 +08:00
    @shazh520 蛋疼,你开始代码太乱没看。
    你打断点看下就知道了,remove 不是删除那个元素,而是删除那个下标,所以一直在删除 第 1 个 (从 0 开始)元素,然后你 i 会执行 50 次,就是做了 50 次删除第一个元素。唉,还反编译
    zpxshl
        12
    zpxshl   2018-06-22 22:39:50 +08:00 via Android
    好吧我错了,少看到个 break...
    shazh520
        13
    shazh520   2018-06-22 22:41:07 +08:00
    @zpxshl 编译器优化了那个循环,是对应的
    lonenol
        14
    lonenol   2018-06-22 22:41:56 +08:00
    第一次删了 0,变成 1-99
    然后每次都是 remove(1),删第二个元素,保留了 1
    就变成你看到的那样了。。
    shazh520
        15
    shazh520   2018-06-22 22:45:37 +08:00
    @lhx2008 按照这个说法那最后应该是留下了 0 而不是留下 1 没有删除。循环的第一次执行正确的删除了下标为 0 的元素“ 0 ”,但是后面的每次执行为什么就都在删除下标为 1 的元素了呢?
    shazh520
        16
    shazh520   2018-06-22 22:47:13 +08:00
    @lonenol 我就是搞不明白为啥后面都是删除第二个元素了。 这个太诡异了嘛,没有规律啊。第一次为啥特殊勒,大佬?
    lhx2008
        17
    lhx2008   2018-06-22 22:48:10 +08:00 via Android
    @shazh520 第一次是 0123,第一个数就是 0,当然就把 0 删掉了,后面变成 123,第一个数是 1,就一直删第一个数,1 是第 0 个数
    zpxshl
        18
    zpxshl   2018-06-22 22:48:17 +08:00 via Android   ❤️ 1
    @shazh520 你的 list 从 0 开始,第一次循环删掉下标 0,正好值为 0。 list 变成 1 开始。 第二次-n 次循环,每次都是删掉下标为 1 的数,就是删掉 2,3,4,5...
    qusthuang
        19
    qusthuang   2018-06-22 22:53:14 +08:00   ❤️ 1
    remove(Integer ) 和 remove(int),看下 jdk 实现就知道了
    shazh520
        20
    shazh520   2018-06-22 22:53:27 +08:00
    @lhx2008 [手动笑哭表情] 大佬,我大体猜到你表达的意思了,你前半句中的第一个数指的是下标 0,后半句中的第一个数指的是下标 1 是吧。 但是我就是想问,为啥第一次删除下标 0,第二次删除下标 1,第三次删除下标 1,...,没规律啊。
    shazh520
        21
    shazh520   2018-06-22 22:54:52 +08:00
    @qusthuang
    @zpxshl 大佬,我结合你们两个的回答我找到答案了 哈哈 谢谢谢谢
    lhx2008
        22
    lhx2008   2018-06-22 22:55:26 +08:00 via Android   ❤️ 1
    @shazh520 你的代码可以简写成
    50 次循环
    renove(test[0])
    第一次 test[0] 是 0
    后面都是 1
    VoidChen
        23
    VoidChen   2018-06-23 10:52:46 +08:00
    @shazh520 其实 @lhx2008 早就说出了答案了,你还一直没看懂。。。
    shazh520
        24
    shazh520   2018-06-24 12:48:21 +08:00
    @VoidChen 对的 哈哈
    shazh520
        25
    shazh520   2018-06-24 12:50:45 +08:00
    如果代码是这样结果就完全不一样了,就像是 @qusthuang 说的那样,删除对象和下标的问题。
    package com.sunhao.seckill;

    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;

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

    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class SeckillApplicationTests {

    public static void main(String[] args) {
    List<Integer> integerList = new ArrayList<>();

    for (int i = 0; i < 100; i++) {
    integerList.add(i);
    }

    System.out.println(integerList);

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

    System.out.println(integerList);
    }

    }
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3166 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 11:37 · PVG 19:37 · LAX 04:37 · JFK 07:37
    ♥ Do have faith in what you're doing.