RFR: 8029795 : LinkedHashMap.getOrDefault() doesn't update access order. (was Why doesn't new Map methods generate entry accesses on LinkedHashMap?)

Paul Sandoz paul.sandoz at oracle.com
Wed Dec 11 10:32:19 UTC 2013


On Dec 11, 2013, at 1:27 AM, Mike Duigou <mike.duigou at oracle.com> wrote:

> I have added tests and documentation for the other methods.
> 
> http://cr.openjdk.java.net/~mduigou/JDK-8029795/1/webrev/
> 
> The documentation for some of the methods is ambiguous about how many access events are generated. For LRU cache is OK but other cases (counting based eviction) may care about the total number of accesses.

+ * invocation completes).  Invoking the {@code replace}, {@code merge},
+ * {@code compute} or {@code computeIfPresent} methods results in one or more
+ * accesses of the corresponding entry. The {@code putAll} method generates one

If i look at the code for say replace it generates *at most one access* in terms of affecting last access order, if the entry exists and if the old/new value constraint holds:

    @Override
    public boolean replace(K key, V oldValue, V newValue) {
        Node<K,V> e; V v;
        if ((e = getNode(hash(key), key)) != null &&
            ((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
            e.value = newValue;
            afterNodeAccess(e);
            return true;
        }
        return false;
    }


I have attempted to categorise the access behaviour of each method:

get:
  access to entry, if it exists

put:
  access to new entry

putIfAbsent
  access to entry, if it exists
  otherwise access to new entry

replace(K key, V value)
  access to entry, if it exists

replace(K key, V oldValue, V newValue)
  access to entry, if it exists and it's value is updated

computeIfAbsent:
  access to entry, if it exists and it's value is non-null, or 
  access to entry, if it exists and it's null value is updated to a non-null value [*], 
  otherwise access to new entry, if created

computeIfPresent
  access to entry, if it exists and it's non-null value is updated to a non-null value

compute
  access to entry, if it exists and it's value is updated to a non-null value,
  otherwise access to new entry, if created

merge
  access to entry, if it exists and it's value is updated to a non-null value,
  otherwise access to new entry, if created


Patterns, to help group for documentation:

get/put/putIfAbsent/replace(K key, V value): access to entry associated with the key, if entry exists after invocation completes

replace(K key, V oldValue, V newValue): access to entry associated with the key, if returns true

computeIfAbsent/computeIfPresent/compute/merge: access to entry associated with the key, if returns a non-null value.


--

[*] There is some ambiguity with computeIfAbsent. When the entry exists, it's value is null, and the value returned from the mapping function is null then, an access to that entry occurs:

        V v = mappingFunction.apply(key);
        if (old != null) {
            old.value = v;
            afterNodeAccess(old);
            return v;
        }
        else if (v == null)
            return null;

Is that a bug? I would have expected the returning of null from the mapping function to signal no-action to be taken. Thus computeIfPresent and computeIfAbsent have no side-effects for an existing entry whose value is null when the function returns null (same applies to merge, if the value parameter is null or the remapping function returns null, and to compute, if the remapping function returns null).

So perhaps that code should be:

        V v = mappingFunction.apply(key);
        if (v == null)
           return null;
        else if (old != null) {
            old.value = v;
            afterNodeAccess(old);
            return v;
        }
 
Paul.


More information about the core-libs-dev mailing list