简体   繁体   English

Iterator.remove() 不删除

[英]Iterator.remove() not removing

I came across a problem while using Iterator's remove() method with no Exceptions thrown but the method simply doesn't work as expected.我在使用没有抛出异常的迭代器的remove()方法时遇到了一个问题,但该方法根本无法按预期工作。

There is a Map<List<Integer>, Integer> which contains:有一个Map<List<Integer>, Integer>包含:

{[1, 2]=3, [2, -1]=1, [-4]=-4, [-1, 0, 1]=0, [-1, -4]=-5, [0, 1, -1]=0}

And the trouble-prone line is:容易出问题的线路是:

map.entrySet().removeIf(e -> e.getKey().size() < 3 || e.getValue() != 0);

I googled a lot but still can't figure it out.我用谷歌搜索了很多,但仍然无法弄清楚。

The complete code is as follows:完整代码如下:

public class Main {
    public static void main(String[] args){
        int[] nums = {-1, 0, 1, 2, -1, -4};
        Solution sol = new Solution();
        Map<List<Integer>, Integer> map = sol.threeSum(nums);
        Iterator<Map.Entry<List<Integer>, Integer>> it = map.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<List<Integer>, Integer> m = it.next();
            System.out.println(m.getKey().size());
        }
        map.entrySet().removeIf(e -> e.getKey().size() < 3 || e.getValue() != 0);
        System.out.println(map);
    }
}

class Solution {
    public Map<List<Integer>, Integer> threeSum(int[] nums) {
        HashMap<List<Integer>, Integer> map = new HashMap<List<Integer>, Integer>();
        for (int num : nums) {
            Iterator<Map.Entry<List<Integer>, Integer>> it = map.entrySet().iterator();
            while (it.hasNext()) {
                Map.Entry<List<Integer>, Integer> e = it.next();
                if (e.getKey().size() == 2) {
                    if (e.getKey().get(0) + e.getKey().get(1) + num != 0) {
                        it.remove();
                    } else {
                        e.getKey().add(num);
                        e.setValue(0);
                    }
                } else if (e.getKey().size() == 1) {
                    e.getKey().add(num);
                    e.setValue(e.getKey().get(0) + num);
                }
            }
            List<Integer> li = new ArrayList<Integer>();
            li.add(num);
            map.put(li, num);
        }
        System.out.println(map);
        return map;
    }
}

Thanks to @PrasadU I compared the remove method of HashMap, and I find out the problem is caused by the recalculation of hash value in java8 (When I add new element in the list, the hash value of that node changes but the position remains the same, so with the recalculation of the hash we cannot find the node), whereas in java 11 it doesn't recalculate. Thanks to @PrasadU I compared the remove method of HashMap, and I find out the problem is caused by the recalculation of hash value in java8 (When I add new element in the list, the hash value of that node changes but the position remains the同样,因此通过重新计算 hash 我们找不到节点),而在 java 11 中它不会重新计算。

  • java8 java8
public final void remove() {
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            K key = p.key;
            removeNode(hash(key), key, null, false, false);
            expectedModCount = modCount;
        }
  • java11 java11
public final void remove() {
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            removeNode(p.hash, p.key, null, false, false);
            expectedModCount = modCount;
        }

Happy to be corrected -很高兴得到纠正-

There is some issue and a difference in behaviour for JDK 8. It appears that the remove function does not work as expected if the size of the collection is more than 1 in JDK 8. JDK 8 存在一些问题和行为差异。如果集合的大小在 JDK 8 中大于 1,则删除 function 似乎无法按预期工作。

JDK 11 the same code works as expected. JDK 11 相同的代码按预期工作。

I reduced the code to smaller function.我将代码缩减为更小的 function。

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MapRemove2 {
    public static void main(String[] args) {
        testRemove(1);
        testRemove(2);
    }

    private static void testRemove(int limit) {
        System.out.println("LIMIT = " + limit + "\n---------");
        Map<List<Integer>, Integer> map = new HashMap<List<Integer>, Integer>();
        for (int i = 0; i < 5; i++) {
            map.entrySet().removeIf(e -> e.getKey().size() == limit);
            System.out.println(String.format("LOOP %2d - map after remove = %s", i, map));

            int finalI = i;
            map.entrySet().forEach(e -> {
                e.getKey().add(finalI);
                e.setValue(e.getValue() + finalI);
            });

            List<Integer> li = new ArrayList<Integer>();
            li.add(i);
            map.put(li, i);
            System.out.println(String.format("LOOP %2d - map after add = %s", i, map));
        }
        System.out.println("Final MAP " + map + "\n----------\n\n");
    }
}

in JDK 8 1.8.0_212 (compile + run) result is在 JDK 8 1.8.0_212(编译+运行)结果是

> Task :MapRemove2.main()
LIMIT = 1
---------
LOOP  0 - map after remove = {}
LOOP  0 - map after add = {[0]=0}
LOOP  1 - map after remove = {}
LOOP  1 - map after add = {[1]=1}
LOOP  2 - map after remove = {}
LOOP  2 - map after add = {[2]=2}
LOOP  3 - map after remove = {}
LOOP  3 - map after add = {[3]=3}
LOOP  4 - map after remove = {}
LOOP  4 - map after add = {[4]=4}
Final MAP {[4]=4}
----------


LIMIT = 2
---------
LOOP  0 - map after remove = {}
LOOP  0 - map after add = {[0]=0}
LOOP  1 - map after remove = {[0]=0}
LOOP  1 - map after add = {[1]=1, [0, 1]=1}
LOOP  2 - map after remove = {[1]=1, [0, 1]=1}
LOOP  2 - map after add = {[1, 2]=3, [2]=2, [0, 1, 2]=3}
LOOP  3 - map after remove = {[1, 2]=3, [2]=2, [0, 1, 2]=3}
LOOP  3 - map after add = {[1, 2, 3]=6, [2, 3]=5, [3]=3, [0, 1, 2, 3]=6}
LOOP  4 - map after remove = {[1, 2, 3]=6, [2, 3]=5, [3]=3, [0, 1, 2, 3]=6}
LOOP  4 - map after add = {[1, 2, 3, 4]=10, [2, 3, 4]=9, [3, 4]=7, [4]=4, [0, 1, 2, 3, 4]=10}
Final MAP {[1, 2, 3, 4]=10, [2, 3, 4]=9, [3, 4]=7, [4]=4, [0, 1, 2, 3, 4]=10}
----------

in JDK 11 (compile and run)在 JDK 11 中(编译并运行)

Task:MapRemove2.main()任务:MapRemove2.main()

LIMIT = 1
---------
LOOP  0 - map after remove = {}
LOOP  0 - map after add = {[0]=0}
LOOP  1 - map after remove = {}
LOOP  1 - map after add = {[1]=1}
LOOP  2 - map after remove = {}
LOOP  2 - map after add = {[2]=2}
LOOP  3 - map after remove = {}
LOOP  3 - map after add = {[3]=3}
LOOP  4 - map after remove = {}
LOOP  4 - map after add = {[4]=4}
Final MAP {[4]=4}
----------


LIMIT = 2
---------
LOOP  0 - map after remove = {}
LOOP  0 - map after add = {[0]=0}
LOOP  1 - map after remove = {[0]=0}
LOOP  1 - map after add = {[1]=1, [0, 1]=1}
LOOP  2 - map after remove = {[1]=1}
LOOP  2 - map after add = {[1, 2]=3, [2]=2}
LOOP  3 - map after remove = {[2]=2}
LOOP  3 - map after add = {[2, 3]=5, [3]=3}
LOOP  4 - map after remove = {[3]=3}
LOOP  4 - map after add = {[3, 4]=7, [4]=4}
Final MAP {[3, 4]=7, [4]=4}
----------

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM