HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Poor man's lazy evaluation in Java 8

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
evaluationjavapoorlazyman

Problem

I have an class that cheaply imitates a lazy evaluation scheme. The class is for representing a file and additionally providing meta data on the file. Some of the meta-data can be expensive to evaluate so I only want to evaluate it when requested, and only evaluate it once.

My implementation is such: All private members are stored in an EnumMap and each member is associated with a value in the enum Field.

private enum Field {
    FILE_CODE
    //, ...
};
private Map lazyAttributes = new EnumMap(Field.class);


The values in the EnumMap are Optionals in case evaluation failed (value not in database etc.) Accessors methods get the member by providing the Field value associated with the member and a Supplier which will evaluate member if it hasn't yet been evaluated.

private  Optional lazyLookup(Field field, Supplier> answer) {
    if (lazyAttributes.containsKey(field)) { // field was already evaluated, get answer.
        return lazyAttributes.get(field);    // unchecked Optional to Optional
    } else {                                 // field is unevaluated;
        Optional result = answer.get();      // evaluate,
        lazyAttributes.put(field, result);   // then store
        return result;                       // unchecked Optional to Optional
    }
}


This is an example of what an accessor looks like.

public Optional getFileCode() {
    return lazyLookup(Field.FILE_CODE, 
            () -> FileCodeKeyWords.apply(getName())); // consider this expensive
}


I am concerned about the unchecked operations in lazyLookup and it seems to be an overly complicated setup. I can provide the rest of the class if requested.

Solution

Your lazy method is not thread safe. If two threads check for the key at the same time they will both find it absent and both create a new object.

Wrapping your EnumMap in a ConcurrentMap will get around this.

private ConcurrentMap lazyThreadSafeAttributes = new ConcurrentHashMap(lazyAttributes);

private  Optional lazyThreadSafeLookup(Field field, Supplier> answer) {
    return lazyThreadSafeAttributes.computeIfAbsent(field, k -> answer.get());
}

Code Snippets

private ConcurrentMap<Field, Optional> lazyThreadSafeAttributes = new ConcurrentHashMap(lazyAttributes);

private <T> Optional<T> lazyThreadSafeLookup(Field field, Supplier<Optional<T>> answer) {
    return lazyThreadSafeAttributes.computeIfAbsent(field, k -> answer.get());
}

Context

StackExchange Code Review Q#114896, answer score: 2

Revisions (0)

No revisions yet.