<p dir="ltr">putIfAbsent was not designed to be compatible with null values. It was designed for the null-incompatible ConcurrentMap interface. I don&#39;t think a null-compatible implementation of CM is possible, BTW.</p>

<p dir="ltr">So the prospects for putIfAbsent in Map are limited from the start. In your analysis you assume that the user cares about the return value (which is where the problem arises) but might this not be the case?</p>

<p dir="ltr">Joe</p>
<div class="gmail_quote">On Mar 27, 2013 1:25 AM, &quot;Peter Levart&quot; &lt;<a href="mailto:peter.levart@gmail.com">peter.levart@gmail.com</a>&gt; wrote:<br type="attribution"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">

  
    
  
  <div bgcolor="#FFFFFF" text="#000000">
    <div>On 03/27/2013 07:40 AM, Joe Bowbeer
      wrote:<br>
    </div>
    <blockquote type="cite">
      <p dir="ltr">I was not expecting Map to define a default impl. for
        what was originally a ConcurrentMap method, btw.</p>
      <p dir="ltr">If it does, however, and if Map supports null - as
        all the base Collections do - then shouldn&#39;t all the default
        method implementations support null as well? (Support is the
        correct term here, as the Collections spec makes no mention of
        tolerate.)</p>
    </blockquote>
    <br>
    The new definition still supports null, but differently.<br>
    <br>
    <blockquote type="cite">
      <p dir="ltr">In Map there is a distinction between !contains(key)
        and get(key)==null. I think putIfAbsent is even defined in terms
        of contains() and not get().</p>
    </blockquote>
    <br>
    It is defined, yes, but the part about null values &quot;wasn&#39;t exploited
    in reality&quot;, since all ConcurrentMap implementations in JDK don&#39;t
    support null values and I haven&#39;t yet seen one that does. Have you?
    The debate is about whether it is appropriate to *change* the
    definition of putIfAbsent. I think it is. Because now, that
    putIfAbsent is being defined for plain Maps which we know do support
    null values, the question about nulls becomes relevant. Which is the
    most appropriate definition of putIfAbsent for Maps that do support
    null values (the only constraint being that method signature should
    not change)? Current definition (as defined until now) is dubious,
    since when putIfAbsent returns null, the user does not know whether
    null has already been mapped and was therefore not replaced with new
    value or there was no mapping yet and new value has been entered
    into the Map. This non-determinism is not making such putIfAbsent
    usable at all. With new definition, the null return is more
    deterministic. You still don&#39;t know whether there was already a
    mapping to null present in the map or there was no mapping, but you
    are certain that new value landed in the Map. I think this is more
    appropriate. In either way, if one wants to use putIfAbsent, he
    should not put null values in the Map.<br>
    <br>
    Regards, Peter<br>
    <br>
    <blockquote type="cite">
      <p dir="ltr">How can we justify not fully supporting null in some
        Map methods?</p>
      <p dir="ltr">I can&#39;t come up with a rationale or a loophole to
        exploit.</p>
      <div class="gmail_quote">On Mar 27, 2013 12:01 AM, &quot;Mike Duigou&quot;
        &lt;<a href="mailto:mike.duigou@oracle.com" target="_blank">mike.duigou@oracle.com</a>&gt;
        wrote:<br type="attribution">
        <blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
          <div style="word-wrap:break-word">I&#39;ve pushed another update
            that has putIfAbsent treat null values as absent. This
            allows putIfAbsent to be restored in a couple of the
            defaults. This feels like the right thing to do though some
            null-lover will eventually be unhappy that his null value
            got trampled. The remove() and replace() methods still
            distinguish between absent keys and present keys with null
            values. I think this is appropriate.
            <div>
              <div><br>
              </div>
              <div>The alternative to treating null values as absent is
                to add additional code to distinguish between absent and
                nulls values everywhere. I attempted this in 
                <div><br>
                </div>
                <div><a href="http://hg.openjdk.java.net/lambda/lambda/jdk/rev/8561d74a9e8f" target="_blank">http://hg.openjdk.java.net/lambda/lambda/jdk/rev/8561d74a9e8f</a><br>
                  <div><br>
                  </div>
                  <div>It wasn&#39;t much more successful because the value
                    mappers use null for signalling.</div>
                  <div>
                    <div><br>
                    </div>
                    <div>The current specification and behaviour seems
                      about as good as can be achieved while still
                      tolerating nulls and without introducing Optional.</div>
                    <div><br>
                    </div>
                    <div>
                      <div>Mike<br>
                        <div><br>
                          <div>
                            <div>On Mar 25 2013, at 16:41 , Mike Duigou
                              wrote:</div>
                            <br>
                            <blockquote type="cite">
                              <div style="word-wrap:break-word">My
                                intention with the first cut of the unit
                                test was to avoid changing the method
                                specification while having consistent
                                behaviour across all of the Map
                                implementations. It&#39;s certainly possible
                                I made mistakes and changed the contract
                                of the defaults in ways I didn&#39;t
                                intend. 
                                <div>
                                  <br>
                                </div>
                                <div>
                                  <div>I think perhaps that changing
                                    putIfAbsent to allow replacement of
                                    null values would help a lot to
                                    making the operations more
                                    consistent. I am also going to look
                                    at Sam&#39;s suggestion of using
                                    replace() rather than put() in a few
                                    places. Using replace() almost
                                    ensures though that we&#39;ll introduce
                                    more retry loop implementations.
                                    This may be fine though.</div>
                                  <div><br>
                                  </div>
                                  <div>Mike</div>
                                  <div><br>
                                  </div>
                                  <div><br>
                                    <div>
                                      <div>On Mar 25 2013, at 14:57 ,
                                        Peter Levart wrote:</div>
                                      <br>
                                      <blockquote type="cite">
                                        <div bgcolor="#FFFFFF" text="#000000"> Hi Mike,<br>
                                          <br>
                                          I see default
                                          Map.computeIfAbsent has been
                                          chosen to not be atomic and
                                          rather support null values
                                          more naturally. That&#39;s ok if
                                          JDK ConcurrentMap
                                          implementations provide atomic
                                          overrides. Other 3rd party
                                          implementations will follow
                                          soon.<br>
                                          <br>
                                           But I have doubts about
                                          default Map.compute(). On one
                                          hand it contains the usual
                                          disclaimer:<br>
                                          <br>
                                                900      * &lt;p&gt;The
                                          default implementation makes
                                          no guarantees about<br>
                                                901      *
                                          synchronization or atomicity
                                          properties of this method or
                                          the<br>
                                                902      * application
                                          of the remapping function. Any
                                          class overriding<br>
                                                903      * this method
                                          must specify its concurrency
                                          properties.  In<br>
                                                904      * particular,
                                          all implementations of
                                          subinterface {@link<br>
                                                905      *
                                          java.util.concurrent.ConcurrentMap}
                                          must document whether the<br>
                                                906      * function is
                                          applied exactly once
                                          atomically. Any class that<br>
                                                907      * permits null
                                          values must document whether
                                          and how this method<br>
                                                908      * distinguishes
                                          absence from null mappings.<br>
                                          <br>
                                          ...but on the other hand it
                                          tries to be smart:<br>
                                          <br>
                                                924      * In concurrent
                                          contexts, the default
                                          implementation may retry<br>
                                                925      * these steps
                                          when multiple threads attempt
                                          updates.<br>
                                          <br>
                                          Now this last sentence may
                                          indicate that a ConcurrentMap
                                          implementation that does not
                                          override compute() might
                                          safely be used with default
                                          Map.compute() and be atomic.
                                          It was atomic until
                                          putIfAbsent() was replaced
                                          with plain put() to correct
                                          the &quot;existing null value&quot;
                                          behavior. The retry-loop is
                                          only needed when the
                                          optimistic operation is not
                                          successful. But put() is not
                                          optimistic. It always
                                          &quot;succeeds&quot;. And retrying after
                                          the put() only makes things
                                          worse: &quot;Oh, somebody was
                                          quicker than me and I have
                                          just overwritten his value -
                                          never mind, I&#39;ll try to make
                                          some more damage in next
                                          loop...&quot;<br>
                                          <br>
                                          If the damage was done
                                          already, then there&#39;s no point
                                          in repeating the loop.
                                          Further, what&#39;s the point in
                                          using optimistic operations:
                                          replace(key, oldValue,
                                          newValue) and remove(key,
                                          oldValue) with retry-loop on
                                          one hand and then just
                                          stomping over with put() on
                                          the other hand. If the default
                                          Map.compute() method is
                                          declared non-atomic, then
                                          plain put(key, newValue)
                                          instead of replace(key,
                                          oldValue, newValue) and
                                          remove(key) instead of
                                          remove(key, oldValue) could be
                                          used and no retry-loop...<br>
                                          <br>
                                          The same goes for default
                                          Map.merge().<br>
                                          <br>
                                          Regards, Peter<br>
                                          <br>
                                          P.S. What do you think about
                                          changing the specification of
                                          putIfAbsent to always
                                          overwrite null values? It
                                          could make all these things
                                          simpler and more consistent.
                                          And it would not break
                                          anything.<br>
                                          <br>
                                          <br>
                                          <div>On 03/25/2013 08:21 PM, <a href="mailto:mike.duigou@oracle.com" target="_blank">mike.duigou@oracle.com</a>
                                            wrote:<br>
                                          </div>
                                          <blockquote type="cite">
                                            <pre>Changeset: c8d40b7e6de3
Author:    mduigou
Date:      2013-03-20 20:32 -0700
URL:       <a href="http://hg.openjdk.java.net/lambda/lambda/jdk/rev/c8d40b7e6de3" target="_blank">http://hg.openjdk.java.net/lambda/lambda/jdk/rev/c8d40b7e6de3</a>

bug fixes and unit test for Map Defaults

! src/share/classes/java/util/HashMap.java
! src/share/classes/java/util/Map.java
+ test/java/util/Map/Defaults.java


</pre>
                                          </blockquote>
                                          <br>
                                        </div>
                                      </blockquote>
                                    </div>
                                    <br>
                                  </div>
                                </div>
                              </div>
                            </blockquote>
                          </div>
                          <br>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </blockquote>
      </div>
    </blockquote>
    <br>
  </div>

</blockquote></div>