JDK-8224731 : Javadocs for java 8 version of HashMap.computeIfAbsent() is incorrect.
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Affected Version: 8
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2019-05-22
  • Updated: 2019-05-24
  • Resolved: 2019-05-24
Related Reports
Duplicate :  
Description
ADDITIONAL SYSTEM INFORMATION :
RHEL 7.5. Java 8.

A DESCRIPTION OF THE PROBLEM :
The java 9 Javadoc for HashMap.computeIfAbsent() states that "The mapping function should not modify this map during computation". This is an important caveat. Unfortunately the java 8 javadocs do not include this important warning despite the fact that the method is impacted by the same issue. Worse still, the java 8 implementation of the method does not throw a ConcurrentModificationException if a mapping function modifies the map - instead it can silently result in bad entries in the Map.

REGRESSION : Last worked in version 8u192

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached source on java 8. It prints out many lines of text because the contents of the Map are not as expected. On Java 11 it crashes with a ConcurrentModificationException message (which is not strictly correct since it is a single-threaded process).

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test should either work or it should fail with an error or there should be a comment in the java 8 javadocs to indicate the potential problem.
ACTUAL -
The test fails when loadFactor is set above 1.0.

---------- BEGIN SOURCE ----------
import java.util.HashMap;

public class CIF {
        public static HashMap<Integer, Integer> hMap = new HashMap<Integer, Integer>(2048, 100.0f);
        public static void main(String[] args) throws Exception {

                for (int i=0; i<100000; i++) {
                        hMap.computeIfAbsent(i, k -> f(k));
                        // System.out.println("(2) " + i + " " + hMap.get(i));
                }
                for (int i=0; i<200000; i++) {
                        if (hMap.get(i) != i) {
                                System.out.println(i + " -> " + hMap.get(i));
                        }
                }
        }

        static int f(int idx) {
                idx += 100000;
                hMap.computeIfAbsent(idx, k -> f1(k));
                return idx-100000;
        }

        static int f1(int idx) {
                hMap.computeIfAbsent(idx-100000, k -> f2(k));
                // System.out.println("(1) " + (idx-100000) + " " + hMap.get(idx-100000));
                return idx;
        }

        static int f2(int idx) {
                return idx+1;
        }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Ensure that we do not modify the map inside the mapping function - which is not a limitation mentioned in the javadocs.

FREQUENCY : always