简体   繁体   中英

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.

There is a Map<List<Integer>, Integer> which contains:

{[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.

  • 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
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 11 the same code works as expected.

I reduced the code to smaller 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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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