JDK-8259622 : TreeMap.computeIfAbsent deviates from spec
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Affected Version: 15
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2021-01-11
  • Updated: 2021-04-06
  • Resolved: 2021-01-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 17
17 b06Fixed
Related Reports
Relates :  
Sub Tasks
JDK-8261476 :  
Description
A DESCRIPTION OF THE PROBLEM :
Mapping a key-value to `null` in a TreeMap and then invoking computeIfAbsent with that key will return null instead of the provided default value and also fail to establish the new mapping.

Appears to have been caused by https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8176894

Spec: "If the specified key is not already associated with a value (or is mapped to null)"
Interface default impl: `if ((v = get(key)) == null)`

REGRESSION : Last worked in version 14

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
see test case

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
all four lines printed should say "default"
ACTUAL -
the treemap maintains the mapping to `null`

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

class Scratch {
	public static void main(String[] args) {
		TreeMap treemap = new TreeMap();
		treemap.put("a", null);
		System.out.println(treemap.computeIfAbsent("a", key -> "default"));
		System.out.println(treemap.get("a"));

		HashMap hashmap = new HashMap();
		hashmap.put("a", null);
		System.out.println(hashmap.computeIfAbsent("a", key -> "default"));
		System.out.println(hashmap.get("a"));
	}
}
---------- END SOURCE ----------

FREQUENCY : always



Comments
No response from the submitter.
06-04-2021

Requested the submitter to verify the fix with the latest version of JDK at https://jdk.java.net/17/
10-03-2021

Should we backport this change to 16 or it's too late? Is there backport process description somewhere?
15-01-2021

Changeset: 2c8e337d Author: Tagir F. Valeev <tvaleev@openjdk.org> Date: 2021-01-15 04:11:31 +0000 URL: https://git.openjdk.java.net/jdk/commit/2c8e337d
15-01-2021

Oops, looks like an unpleasant mistake :( I thought I did this carefully but it looks like I missed this particular case. A similar situation seems to be handled correctly in compute, computeIfPresent, putIfAbsent and merge methods. A patch like this should fix the issue: ``` diff --git a/src/java.base/share/classes/java/util/TreeMap.java b/src/java.base/share/classes/java/util/TreeMap.java --- a/src/java.base/share/classes/java/util/TreeMap.java (revision a483869a6a98ccf296763732a32894f6a39fff40) +++ b/src/java.base/share/classes/java/util/TreeMap.java (date 1610528130345) @@ -575,8 +575,12 @@ t = t.left; else if (cmp > 0) t = t.right; - else + else { + if (t.value == null) { + t.value = callMappingFunctionWithCheck(key, mappingFunction); + } return t.value; + } } while (t != null); } else { Objects.requireNonNull(key); @@ -589,8 +593,12 @@ t = t.left; else if (cmp > 0) t = t.right; - else + else { + if (t.value == null) { + t.value = callMappingFunctionWithCheck(key, mappingFunction); + } return t.value; + } } while (t != null); } newValue = callMappingFunctionWithCheck(key, mappingFunction); ``` I can try to handle this, though I still haven't tried new scara workflow
13-01-2021

Seems to be the usual confusion between a mapping with a null value and the absence of a mapping. [~tvaleev] Any thoughts on this?
12-01-2021

The observations on Windows 10: JDK 15-ea+17: Passed JDK 15-ea+18: Failed, the treemap maintains the mapping to `null`
12-01-2021