Java list reverse search best practice

I saw it on a blog post that the following was a smart way to do a “reverse lookup” using getCode(int) in a Java enumeration:

 public enum Status { WAITING(0), READY(1), SKIPPED(-1), COMPLETED(5); private static final Map<Integer,Status> lookup = new HashMap<Integer,Status>(); static { for(Status s : EnumSet.allOf(Status.class)) lookup.put(s.getCode(), s); } private int code; private Status(int code) { this.code = code; } public int getCode() { return code; } public static Status get(int code) { return lookup.get(code); } } 

For me, a static map and a static initializer look like a bad idea, and my first thought was to encode the search like this:

 public enum Status { WAITING(0), READY(1), SKIPPED(-1), COMPLETED(5); private int code; private Status(int code) { this.code = code; } public int getCode() { return code; } public static Status get(int code) { for(Status s : values()) { if(s.code == code) return s; } return null; } } 

Are there any obvious problems with any method, and is there a recommended way to implement this kind of search?

+71
java enums static-initializer
Mar 15 '11 at 18:30
source share
8 answers

Maps.uniqueIndex from Google Guava is very convenient for creating reference maps.

Update: here is an example of using Maps.uniqueIndex with Java 8:

 public enum MyEnum { A(0), B(1), C(2); private static final Map<Integer, MyEnum> LOOKUP = Maps.uniqueIndex( Arrays.asList(MyEnum.values()), MyEnum::getStatus ); private final int status; MyEnum(int status) { this.status = status; } public int getStatus() { return status; } @Nullable public static MyEnum fromStatus(int status) { return LOOKUP.get(status); } } 
+24
Mar 15 '11 at 18:33
source share
— -

Although it has higher overhead, a static map is good because it offers constant time searching on code . The runtime of your implementation increases linearly with the number of elements in the enumeration. For small listings, this simply will not make a significant contribution.

One problem with both implementations (and possibly with the Java enumeration in general) is that there really is a hidden optional value that can: null . Depending on the rules of business logic, it may make sense to return the actual value of the enumeration or throw an Exception when the search "failed."

+20
Mar 15 '11 at 18:34
source share

Here is an alternative that can even be a little faster:

 public enum Status { WAITING(0), READY(1), SKIPPED(-1), COMPLETED(5); private int code; private Status(int code) { this.code = code; } public int getCode() { return code; } public static Status get(int code) { switch(code) { case 0: return WAITING; case 1: return READY; case -1: return SKIPPED; case 5: return COMPLETED; } return null; } } 

Of course, this is not very convenient if you want to add more constants later.

+8
Mar 15 '11 at 18:40
source share

Obviously, the map will provide a constant search for time, while the cycle will not. In a typical multi-value enumeration, I see no problem finding a workaround.

+5
Mar 15 '11 at 18:36
source share

Here is an alternative to Java 8 (with unit test):

 // DictionarySupport.java : import org.apache.commons.collections4.Factory; import org.apache.commons.collections4.map.LazyMap; import java.util.HashMap; import java.util.Map; public interface DictionarySupport<T extends Enum<T>> { @SuppressWarnings("unchecked") Map<Class<?>, Map<String, Object>> byCodeMap = LazyMap.lazyMap(new HashMap(), (Factory) HashMap::new); @SuppressWarnings("unchecked") Map<Class<?>, Map<Object, String>> byEnumMap = LazyMap.lazyMap(new HashMap(), (Factory) HashMap::new); default void init(String code) { byCodeMap.get(this.getClass()).put(code, this); byEnumMap.get(this.getClass()).put(this, code) ; } static <T extends Enum<T>> T getByCode(Class<T> clazz, String code) { clazz.getEnumConstants(); return (T) byCodeMap.get(clazz).get(code); } default <T extends Enum<T>> String getCode() { return byEnumMap.get(this.getClass()).get(this); } } // Dictionary 1: public enum Dictionary1 implements DictionarySupport<Dictionary1> { VALUE1("code1"), VALUE2("code2"); private Dictionary1(String code) { init(code); } } // Dictionary 2: public enum Dictionary2 implements DictionarySupport<Dictionary2> { VALUE1("code1"), VALUE2("code2"); private Dictionary2(String code) { init(code); } } // DictionarySupportTest.java: import org.testng.annotations.Test; import static org.fest.assertions.api.Assertions.assertThat; public class DictionarySupportTest { @Test public void teetSlownikSupport() { assertThat(getByCode(Dictionary1.class, "code1")).isEqualTo(Dictionary1.VALUE1); assertThat(Dictionary1.VALUE1.getCode()).isEqualTo("code1"); assertThat(getByCode(Dictionary1.class, "code2")).isEqualTo(Dictionary1.VALUE2); assertThat(Dictionary1.VALUE2.getCode()).isEqualTo("code2"); assertThat(getByCode(Dictionary2.class, "code1")).isEqualTo(Dictionary2.VALUE1); assertThat(Dictionary2.VALUE1.getCode()).isEqualTo("code1"); assertThat(getByCode(Dictionary2.class, "code2")).isEqualTo(Dictionary2.VALUE2); assertThat(Dictionary2.VALUE2.getCode()).isEqualTo("code2"); } } 
+3
May 11 '15 at
source share

In Java 8, I just add the following factory method to your listing and skip the search map.

 public static Optional<Status> of(int value) { return Arrays.stream(values()).filter(v -> value == v.getCode()).findFirst(); } 
0
Apr 21 '17 at 7:19
source share

Every other answer in this thread used a static method. I thought we should provide an example using the instance method, as an alternative:

 public class Main { public static void main(String[] args) { for (Alphabet item : Alphabet.values()) { System.out.println("GET: " + item.getValue() ); System.out.println("REV: " + item.reverseLookup(item.getValue()) ); } } } enum Alphabet { X(24) { public String reverseLookup(int o) { return "X"; } }, Y(25) { public String reverseLookup(int o) { return "Y"; } }, Z(26) { public String reverseLookup(int o) { return "Z"; } }; abstract String reverseLookup(int o); private final int value; Alphabet(final int newValue) { value = newValue; } public int getValue() { return value; } } 
0
May 13 '19 at 18:59
source share

Both methods are perfectly true. And they have technically the same Big-Oh uptime.

However, if you first save all the values ​​on the map, you will save time spent repeating this set each time you want to perform a search. So, I think the static map and initializer are a little better.

-one
Mar 15 '11 at 18:35
source share



All Articles