43

I need an EnumSet from an array (which is given through a varargs method parameter). First, I was surprised that there is no varargs constructor method in EnumSet (there is EnumSet#of(E first, E... rest)). As a workaround, I used the following variant:

EnumSet<Options> temp = EnumSet.copyOf(Arrays.asList(options));

However, this triggers a java.lang.IllegalArgumentException: Collection is empty. So, now I ended up with the following, which looks somewhat ridiculous:

EnumSet<Options> temp = options.length > 0 ? 
                        EnumSet.copyOf(Arrays.asList(options)) : 
                        EnumSet.noneOf(Options.class);

If course this could be moved to some utility method, but still, I'm asking myself if there is a simpler way using existing methods?

qqilihq
  • 10,794
  • 7
  • 48
  • 89
  • 1
    I just wondered why this is not supported out of the box. Maybe this worth to create a ticket in OpenJDK? – Sergey Ponomarev Oct 26 '18 at 09:14
  • 5
    The reason is that with a method like `of(E... all)` it may fail to determine the runtime type if the array is empty. But that just makes one wonder why there is no method like `of(Class type, E... all)` – Mark Jeronimus Oct 31 '18 at 08:52
  • @MarkJeronimus You can get the type from an empty array via `Class.getComponentType()`. – shmosel Mar 31 '22 at 07:12

5 Answers5

27

This is two lines, but slightly less complex:

EnumSet<Options> temp = EnumSet.noneOf(Options.class); // make an empty enumset
temp.addAll(Arrays.asList(options)); // add varargs to it

I don't consider this worse than any other kind of variable declaration for a class which doesn't have the constructor you want:

    SomeClass variable = new SomeClass(); // make an empty object
    variable.addStuff(stuff); // add stuff to it
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
azurefrog
  • 10,785
  • 7
  • 42
  • 56
26

Just an alternative. Same amount of code, except no need converting to list, using EnumSet.of() :

EnumSet<Options> temp = options.length > 0 ? 
                        EnumSet.of(options[0], options) : 
                        EnumSet.noneOf(Options.class);

No worries that first element is repeated(it won't be duplicated in a set anyway), no performance gain or penalties as well.

Joel Jonsson
  • 800
  • 4
  • 17
kiruwka
  • 9,250
  • 4
  • 30
  • 41
14

Guava has factory methods for these kinds of situations: It still needs the call to Arrays.asList but at least it's readable.

import com.google.common.collect.Sets;

EnumSet<Options> temp = Sets.newEnumSet(Arrays.asList(options), Options.class);
Johan Tidén
  • 664
  • 7
  • 19
  • Why adding a third party library just to make exactly what could already been done with the Java standard API in 2014? dkarp's answer is better to me because it doesn't use a library, it doesn't create an intermediary list and I find it readable even though the single line of code is a bit longer. Moreover, Guava has a serious lack of modularity which might refrain some developers from using it. – gouessej Feb 06 '18 at 10:49
  • 5
    @gouessej While the points in your comment are valid, this Answer is still helpful and informative. Seems especially on point as the Question specifically mentions surprise at the lack of a no varargs constructor – this Answer with Guava gives the same effect, a single line with similar syntax and readability. I upvoted *all* of the Answers on this page as they were all clever or informative. – Basil Bourque Apr 25 '18 at 04:05
  • @BasilBourque I'll go on downvoting answers that use third party dependencies in any context in which it isn't required (2014, Java 1.8 available) and in which it's not explicitly asked by the original poster (no "guava" tag, no mention of Guava in the question). – gouessej Apr 26 '18 at 10:54
  • 4
    To be fair. There is nothing in the question stating that they have java 1.8. Also, library answers might be useful for readers that already happen to use that lib. I would assume that readers are mature enough to make their own decision whether or not to include a new lib. – Johan Tidén Apr 26 '18 at 11:53
10

You can also do this with Java8 streams and your own CollectionFactory:

EnumSet<Options> temp = Arrays.stream(options)
        .collect(Collectors.toCollection(() -> EnumSet.noneOf(Options.class)));
dkarp
  • 14,483
  • 6
  • 58
  • 65
5

The simplest solution is the obvious one: If you have access to the method's signature you are using then just mimic the signature of EnumSet.of(T first, T ... more) as

void myMethod(Options first, Options ... rest) {
    EnumSet<Options> temp = EnumSet.of(first, rest);
}

As the ellipsis is already the last element of your method's parameters list (because it has to be) this won't change any calling code and you don't need to jump through loops.

buræquete
  • 14,226
  • 4
  • 44
  • 89
Scheintod
  • 7,953
  • 9
  • 42
  • 61