Search code examples
javaenumsvolatilelazy-initialization

Can I use volatile on a collection?


I have an enum and I think I can cache the result of values().

enum MyEnum {

    SOME;

    private static volatile Set<MyEnum> values_;

    public static Set<MyEnum> values_() {
        Set<MyEnum> result = values_;
        if (result == null) {
            values_ = result = Collections.unmodifiableSet(EnumSet.allOf(MyEnum.class));
        }
        return result;
    }
}

I tried the lazy initialization which permits duplicate initializations.

And the sonalint complains on volatile Set<MyEnum> part.

SonaLint: Use thread-safe type; adding "volatile" is not enough to make this field thread-safe.

https://rules.sonarsource.com/java/RSPEC-3077

Is there any problem with my code?


Solution

  • Set.of ( MyEnum.values() )

    Seems much simpler to just statically feed an array from Enum#values() into the Set.of method to create an unmodifiable set.

    See this code run live at Ideone.com.

    enum Animal {
        DOG, CAT, BIRD ;
    
        public static final Set < Animal > values = Set.of ( Animal.values() ) ;  // A cached set of all enum objects.
    }
    

    Being static means this code runs when the class loads. So thread-safe. No need for volatile.

    Being final means the reference variable cannot be changed to point to another set. The original set assigned will be the only set during the run of your app.

    Being an unmodifiable set, you can offer access directly to that static field rather than through a method.

    The Set.of method is free to return an object of a concrete class of its choosing. Current or future implementations of Java may choose to use a class such as EnumSet for efficiency. The implementation of Java 12 utilized at Ideone.com uses the class java.util.ImmutableCollections.SetN, not that we really care.

    Usage:

    Set < Animal > values = Animal.values ; 
    

    [DOG, BIRD, CAT]

    You said:

    lazy initialization

    No need to be lazy, to delay initialization, for such a trivial amount of work as making a Set from the Enum.values() array.


    But I do not recommend this.

    Your intention to cache this set seems to fall into the trap of premature optimization. See this Comment by Brian Goetz.

    Programmers are notoriously poor at intuiting bottlenecks. And modern JVMs are some of the most highly optimized software ever built.

    I suggest just letting the client programmer call the usual Enum#values().