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

Nested HashMap.computeIfAbsent leads to discrepancy size() vs. content

    Details

      Description

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

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 6.1.7601]

      A DESCRIPTION OF THE PROBLEM :
      Consider the attached example application that demonstrates the issue.

      Having a HashMap with two computeIfAbsent calls with different keys having the same hashCode (but not equals).

      For HashMap there is nothing specified about attempting to update other mappings of the map while in the computation function.
      E.g. ConcurrentHashMap does not allow this (see javadoc: "computation should be short and simple, and must not attempt to update any other mappings of this map").

      Eventhough the second item is not inserted but the size() of the map is increased.

      See also for a similar issue with the ConcurrentHashMap:
      https://bugs.openjdk.java.net/browse/JDK-8062841
      https://bugs.openjdk.java.net/browse/JDK-8074374


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      the second item is successfully inserted in the map and also returned by the iterator.

      OR

      there should be an exception indicating the wrong usage (like e.g. ConcurrentModificationException)

      ACTUAL -
      The map reports a size() of 2 but iterating over the entries does only result in one item.


      REPRODUCIBILITY :
      This bug can be reproduced always.

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

      public class ComputeIfAbsentDemo {

      public static void main(String[] args) {
      Map<Key, String> m = new HashMap<>();
      m.computeIfAbsent(new Key("firstKey"), k -> {
      m.computeIfAbsent(new Key("secondKey"), sk -> "secondKey"); // XXX this nested computeIfAbsent is missing in result but present in size()
      return "firstValue";
      });

      System.out.println("Map.size(): " + m.size()); // size is reported to be 2

      // iterator only returns one item:
      System.out.println("Map.entrySet().toArray().length: "+m.entrySet().toArray().length);
      for(Key k : m.keySet()) {
      System.out.println(k.value);
      }
      }

      private static class Key {
      private final String value;

      private Key(String val) {
      value = val;
      }

      @Override
      public int hashCode() {
      return 1; // ensure we have a collision
      }

      @Override
      public boolean equals(Object obj) {
      if (this == obj) {
      return true;
      }
      if (obj == null) {
      return false;
      }
      if (getClass() != obj.getClass()) {
      return false;
      }
      Key other = (Key) obj;
      return Objects.equals(value, other.value);
      }
      }
      }

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

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                psonal Pallavi Sonal
                Reporter:
                webbuggrp Webbug Group
              • Votes:
                0 Vote for this issue
                Watchers:
                2 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: