Since Java 9 HashMap.computeIfAbsent() throws ConcurrentModificationException on attempt to memoize recursive function results

后端 未结 2 1895
南方客
南方客 2021-02-12 13:00

Today I learned from some JS course what memoization is and tried to implement it in Java. I had a simple recursive function to evaluate n-th Fibonacci number:

l         


        
相关标签:
2条回答
  • 2021-02-12 13:30

    ConcurrentModificationException is thrown because slowFib is modifying multiple keys and values. If you look at Java 9 HashMap.computeIfAbsent() code you will find that the exception is thrown here:

    int mc = modCount;
    V v = mappingFunction.apply(key);
    if (mc != modCount) { throw new ConcurrentModificationException(); }
    

    Each invocation of slowFib attempts to modify values mapped to keys n-1 and n-2.

    The modCount check is not performed in Java 8 HashMap.computeIfAbsent() code. This is a bug in Java 8, your approach doesn't work in all cases as per JDK-8071667 HashMap.computeIfAbsent() adds entry that HashMap.get() does not find which added the modCount check in Java 9:

    If the function supplied to computeIfAbsent adds items to the same HashTable on which the function is called from and the internal table is enlarged because of this, the new entry will be added to the wrong place in the Map's internal table making it inaccessible.

    0 讨论(0)
  • 2021-02-12 13:49

    You can roll your own:

    public static <K, V> V computeIfAbsent(
        Map<K, V> cache, 
        K key,
        Function<? super K, ? extends V> function
    ) {
        V result = cache.get(key);
    
        if (result == null) {
            result = function.apply(key);
            cache.put(key, result);
        }
    
        return result;
    }
    

    This approach assumes you don't have null values. If you do, just change the logic to use Map.containsKey()

    This access works around the problem by making the cache value retrieval and putting of calculated values non-atomic again, which Map::computeIfAbsent tries to avoid. I.e. the usual race condition problems re-occur. But that might be fine in your case, of course.

    0 讨论(0)
提交回复
热议问题