Emilien Brigand
Emilien Brigand

Reputation: 10981

Stream.of VS Arrays.stream to get an enum from a value

With Java 8 I found a common way to get an enum from a value, it's using an Arrays.stream with a filter on all the enum values, but recently, I came across another way to do it, with Stream.of, what is the better way between each other and why? Is there another better way to do it?

Example:

public enum Foo {

    BAR_1("Bar 1"),
    BAR_2("Bar 2");

    private String friendlyValue;

    Foo(String friendlyValue){
        this.friendlyValue = friendlyValue;
    }

    public String getFriendlyValue() {
        return friendlyValue;
    }

    public static Foo fromFriendlyValue1(String friendlyValue){
        return Stream.of(Foo.values()).filter(r -> r.getFriendlyValue().equals(friendlyValue)).findFirst().get();
    }

    public static Foo fromFriendlyValue2(String friendlyValue) {
        return Arrays.stream(Foo.values()).filter(r -> r.getFriendlyValue().equals(friendlyValue)).findFirst().get();
    }
}

Upvotes: 4

Views: 1703

Answers (3)

Markus Mikkolainen
Markus Mikkolainen

Reputation: 3497

public enum Foo {

  BAR_1("Bar 1"),
  BAR_2("Bar 2");

  private static class Holder {
    static Map<String, Foo> map = new HashMap<String, Foo>()
       {{
         for(Foo f:Foo.values())
         {
            put(f.getFriendlyValue(),f);
         }
       }};
  }

  private String friendlyValue;

  Foo(String friendlyValue){
    this.friendlyValue = friendlyValue;
  }

  public String getFriendlyValue() {
    return friendlyValue;
  }

  public static Foo fromFriendlyValue(String friendlyValue){
    return Holder.map.get(friendlyValue);
  }
}

This lazy initializes , unlike @bohemian's solution. And the magic here is an anonymous class extending hashmap, executing its anynymous constructor to loop all values of Foo into the map.

Although for most enums (which have a very limited amount of constants) , the simplest solution is just to loop them all.

public enum Foo {

  BAR_1("Bar 1"),
  BAR_2("Bar 2");

  private String friendlyValue;

  Foo(String friendlyValue){
    this.friendlyValue = friendlyValue;
  }

  public String getFriendlyValue() {
    return friendlyValue;
  }

  public static Foo fromFriendlyValue(String friendlyValue){
    for(Foo f:Foo.values())
    {
      if(f.getFriendlyValue().equals(friendlyValue))
          return f;
    }
    return null;
  }
}

Since the cost of just doing the comparison is next to nothing for small enums.

Upvotes: -1

Bohemian
Bohemian

Reputation: 425033

The answer is neither. The cleanest, 1337 way is:

public enum Foo {

    BAR_1("Bar 1"),
    BAR_2("Bar 2");

    private static class Holder {
        static Map<String, Foo> map = new HashMap<String, Foo>();
    }

    private String friendlyValue;

    Foo(String friendlyValue){
        this.friendlyValue = friendlyValue;
        Holder.map.put(friendlyValue, this);
    }

    public String getFriendlyValue() {
        return friendlyValue;
    }

    public static Foo fromFriendlyValue(String friendlyValue){
        return Holder.map.get(friendlyValue);
    }
}

This implementation of fromFriendlyValue() has O(1) time complexity - ie it performs in constant time no matter how large the number of instances in your enum.

The kung fu here is the use of the Initialization-on-demand holder idiom, which neatly side-steps the problem of the initialization rules of an enum while maintaining encapsulation.

Upvotes: 0

Manikandan
Manikandan

Reputation: 3165

Stream.of is actually using Arrays.stream.

public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

So you can directly use Arrays.stream.

Upvotes: 7

Related Questions