Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8186171

HashMap: Entry.setValue may not work after Iterator.remove() called for previous entries

XMLWordPrintable

    • b26
    • generic
    • generic
    • Verified

        FULL PRODUCT VERSION :
        java version "1.8.0_144"
        Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
        Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

        A DESCRIPTION OF THE PROBLEM :
        Javadoc allows to use Iterator.remove() and Entry.setValue() when iterating over HashMap:
        "If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation, or through the setValue operation on a map entry returned by the iterator) the results of the iteration are undefined."

        However it's possible that setValue() doesn't work if remove() was called for some of previous entries. It may happen when remove() leads to untreeify() call - some TreeNodes are replaced by Nodes, but iterator continues to iterate over old instances hence setValue changes the value in the replaced node. (Test case attached)

        REGRESSION. Last worked in version 7u80


        REPRODUCIBILITY :
        This bug can be reproduced always.
        JDK-8186171
        ---------- BEGIN SOURCE ----------
        import java.util.*;

        public class Main {
            static class Key {
                @Override
                public int hashCode() {
                    return 0; // to put keys in one bucket
                }
            }
            public static void main(String[] args) {
                List<Key> keys = new ArrayList<>();
                for (int i = 0; i < 11; ++i) { // 11 is big enough for a map to use tree nodes
                    keys.add(new Key());
                }
                Map<Object, Object> data = Collections.singletonMap(keys.get(10), 1);

                Map<Object, Object> result = new HashMap<>();
                for (Object key : keys) {
                    result.put(key, null);
                }

                for (Iterator<Map.Entry<Object, Object>> iter = result.entrySet().iterator(); iter.hasNext();) {
                    Map.Entry<Object, Object> entry = iter.next();
                    Object value = data.get(entry.getKey());
                    if (value == null) {
                        iter.remove();
                    } else {
                        entry.setValue(value); // this call doesn't set the value
                    }
                }

                if (result.containsValue(null)) {
                    System.out.println("FAILED");
                } else {
                    System.out.println("OK");
                }
            }
        }
        ---------- END SOURCE ----------

              martin Martin Buchholz
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

                Created:
                Updated:
                Resolved: