Why does Java API have seemingly strange assignments inside if statements?

Ivan :

I'm new to programming and Java. I've noticed that, in the Java API, there are methods with strange assignments inside if statements.

Here is an example from the Map interface:

default V replace(K key, V value) {
    V curValue;
    if (((curValue = get(key)) != null) || containsKey(key)) {
        curValue = put(key, value);
    }
    return curValue;
}

Is there some sort of benefit to nesting the assignment this way? Is this purely a style choice? Why not just do the assignment when curValue is first declared?

// why not do it like this?
default V replace(K key, V value) {
    V curValue = get(key); // not nested
    if (curValue != null || containsKey(key)) {
        curValue = put(key, value);
    }
    return curValue;
}

I've noticed this in a lot of the newly added Java 8 methods in the Map interface and elsewhere. This form of nesting the assignment seems unnecessary.

Edit: another example from the Map interface:

default V computeIfAbsent(K key,
        Function<? super K, ? extends V> mappingFunction) {
    Objects.requireNonNull(mappingFunction);
    V v;
    if ((v = get(key)) == null) {
        V newValue;
        if ((newValue = mappingFunction.apply(key)) != null) {
            put(key, newValue);
            return newValue;
        }
    }

    return v;
}
Eugene :

What this is doing is actually copying to a local variable, this is producing smaller byte code, and it is seen as an absolute extreme way of optimization, you will see this in numerous other places in the jdk code.

One other thing is that reading a local variable multiple times, implies reading a shared variable only once, if that for example would have been a volatile and you would read it only once and work with it within the method.

EDIT

The difference between the two approaches is a single read AS FAR AS I CAN TELL

Suppose we have these two methods:

V replace(K key, V value) {
    V curValue;
    if ((curValue = map.get(key)) != null || map.containsKey(key)) {
        curValue = map.put(key, value);
    }
    return curValue;
} 

V replaceSecond(K key, V value) {
    V curValue = map.get(key); // write
    if (curValue != null || map.containsKey(key)) { // read
        curValue = map.put(key, value); // write
    }
    return curValue;
}

The byte code for this is almost identical, except for: replaceSecond is going to have:

 astore_3 // V curValue = map.get(key); store to curValue
 aload_3  // curValue != null; read the value from curValue

While the replace method is going to be:

 dup      // duplicate whatever value came from map.get(key)
 astore_3 // store the value, thus "consuming" it form the stack

In my understanding, dup does not count as yet another read, so I guess this is what is referred as an extreme optimization?

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=439696&siteId=1