基于索引的迭代期间没有 ConcurrentModificationException

Abh*_*nav -2 java iteration loops concurrentmodification

我有以下代码:

public static void main(String[] args) {
 
    List<String> input = new ArrayList<>();
    List<String> output = new ArrayList<>();
    for(int i=0; i< 1000 ;i++){
        input.add(i+"");
    }
    
    
    for(int i=0 ; i<input.size(); i++){
        String value = input.get(i);
        if(Integer.parseInt(value) % 2 == 0){
            output.add(value);
            input.remove(value);
        }
    }
    
    input.stream().forEach(System.out::println);
    System.out.println("--------------------------------------");
    output.stream().forEach(System.out::println);

}
Run Code Online (Sandbox Code Playgroud)

我预计它会抛出,ConcurrentModificationException但它工作正常。有人可以解释一下原因吗?

Jam*_*udd 5

原因是您在技术上没有迭代列表。相反,您使用递增索引随机访问列表,并删除一些值。如果您更改为这样的代码来迭代列表,它将抛出ConcurrentModificationException

\n
public static void main(String[] args) {\n    List<String> input = new ArrayList<>();\n    List<String> output = new ArrayList<>();\n    for(int i=0; i< 1000 ;i++){\n        input.add(i+"");\n    }\n    \n    for (String value : input) {\n        if(Integer.parseInt(value) % 2 == 0){\n            output.add(value);\n            input.remove(value);\n        }\n    }\n\n    input.stream().forEach(System.out::println);\n    System.out.println("--------------------------------------");\n    output.stream().forEach(System.out::println);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

与迭代器相比,为什么这可能不是首选方式的后续内容。原因之一是性能。下面是一些使用 JMH 进行测试的基准代码。

\n
package bench;\n\nimport org.openjdk.jmh.annotations.Benchmark;\nimport org.openjdk.jmh.annotations.BenchmarkMode;\nimport org.openjdk.jmh.annotations.Level;\nimport org.openjdk.jmh.annotations.Measurement;\nimport org.openjdk.jmh.annotations.Mode;\nimport org.openjdk.jmh.annotations.OutputTimeUnit;\nimport org.openjdk.jmh.annotations.Param;\nimport org.openjdk.jmh.annotations.Scope;\nimport org.openjdk.jmh.annotations.Setup;\nimport org.openjdk.jmh.annotations.State;\nimport org.openjdk.jmh.annotations.Warmup;\n\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.concurrent.TimeUnit;\n\nimport static java.util.concurrent.TimeUnit.SECONDS;\n\n@State(Scope.Benchmark)\n@BenchmarkMode(Mode.AverageTime)\n@OutputTimeUnit(TimeUnit.MILLISECONDS)\n@Warmup(iterations = 1, time = 3, timeUnit = SECONDS)\n@Measurement(iterations = 3, time = 2, timeUnit = SECONDS)\npublic class JmhBenchmark {\n    private List<String> input;\n\n    @Param({"100", "1000", "10000"})\n    public int length;\n\n    @Setup(Level.Invocation)\n    public void createInputList() {\n        input = new ArrayList<>();\n        for (int i = 0; i < length; i++) {\n            input.add(i + "");\n        }\n    }\n\n    @Benchmark\n    public void iterateWithVariable() {\n        for (int i = 0; i < input.size(); i++) {\n            String value = input.get(i);\n            if (Integer.parseInt(value) % 2 == 0) {\n                input.remove(value);\n            }\n        }\n    }\n\n    @Benchmark\n    public void iterateWithIterator() {\n        final Iterator<String> iterator = input.iterator();\n        while (iterator.hasNext()) {\n            String value = iterator.next();\n            if (Integer.parseInt(value) % 2 == 0) {\n                iterator.remove();\n            }\n        }\n    }\n\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我的系统的基准测试结果是

\n
Benchmark                         (length)  Mode  Cnt   Score    Error  Units\nJmhBenchmark.iterateWithIterator       100  avgt   15   0.002 \xc2\xb1  0.001  ms/op\nJmhBenchmark.iterateWithIterator      1000  avgt   15   0.033 \xc2\xb1  0.001  ms/op\nJmhBenchmark.iterateWithIterator     10000  avgt   15   1.670 \xc2\xb1  0.017  ms/op\nJmhBenchmark.iterateWithVariable       100  avgt   15   0.005 \xc2\xb1  0.001  ms/op\nJmhBenchmark.iterateWithVariable      1000  avgt   15   0.350 \xc2\xb1  0.014  ms/op\nJmhBenchmark.iterateWithVariable     10000  avgt   15  33.591 \xc2\xb1  0.455  ms/op\n
Run Code Online (Sandbox Code Playgroud)\n

所以我们可以看到使用迭代器从列表中删除一些项目比这个问题提出的方法快很多(>20x)。这是有道理的,您需要在列表中执行随机查找,然后确定是否需要删除它,然后进行另一次查找以查找并删除它。

\n