方便地在enum和int / String之间映射

当处理只能采用有限数量值的variables/参数时,我试着总是使用Java的enum ,如

 public enum BonusType { MONTHLY, YEARLY, ONE_OFF } 

只要我留在我的代码,这工作正常。 但是,我经常需要与其他代码使用纯int (或String )值为同一目的,或者我需要读/写/到数据库的数据存储为数字或string。

在这种情况下,我想有一个方便的方法将每个枚举值与一个整数,这样我可以转换两种方式(换句话说,我需要一个“可逆的枚举”)。

从枚举到int很容易:

 public enum BonusType { public final int id; BonusType(int id) { this.id = id; } MONTHLY(1), YEARLY(2), ONE_OFF(3); } 

然后我可以访问int值为BonusType x = MONTHLY; int id = x.id; BonusType x = MONTHLY; int id = x.id;

但是,我不能看到相反的方法,即从int到enum。 理想情况下,像

 BonusType bt = BonusType.getById(2); 

我能想到的唯一解决scheme是:

  • 将查找方法放入枚举中,该枚举使用BonusType.values()填充地图“int – > enum”,然后将其caching并用于查找。 将工作,但我不得不将这个方法相同地复制到我使用的每个枚举:-(。
  • 将查找方法放入静态实用程序类中。 那么我只需要一个“查找”方法,但是我不得不用一些reflection来让它为任意的枚举工作。

对于这样一个简单的(?)问题,这两种方法都显得非常尴尬。

任何其他想法/见解?

http://www.javaspecialists.co.za/archive/Issue113.html

该解决scheme开始与您的inttypes的枚举定义的一部分类似。 然后,他继续创build一个基于generics的查找实用程序:

 public class ReverseEnumMap<V extends Enum<V> & EnumConverter> { private Map<Byte, V> map = new HashMap<Byte, V>(); public ReverseEnumMap(Class<V> valueType) { for (V v : valueType.getEnumConstants()) { map.put(v.convert(), v); } } public V get(byte num) { return map.get(num); } } 

这个解决scheme很好,并不需要“摆弄reflection”,因为它基于所有枚举types都隐式地inheritance了Enum接口。

enum→int

 yourEnum.ordinal() 

int→枚举

 EnumType.values()[someInt] 

string→枚举

 EnumType.valueOf(yourString) 

枚举→string

 yourEnum.name() 

附注:
正如您正确指出的那样,从版本到版本, ordinal()可能是“不稳定的”。 这就是为什么我总是将常量作为string存储在数据库中的确切原因。 (实际上,当使用MySql时,我将它们存储为MySql枚举 !)

我在网上发现了这个,实现起来非常有帮助和简单。 这个解决scheme不是由我做的

http://www.ajaxonomy.com/2007/java/making-the-most-of-java-50-enum-tricks

 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); } 

}

org.apache.commons.lang.enums.ValuedEnum;

为了节省我编写大量的样板代码或为每个枚举重复代码,我使用了Apache Commons Lang的ValuedEnum代替。

定义

 public class NRPEPacketType extends ValuedEnum { public static final NRPEPacketType TYPE_QUERY = new NRPEPacketType( "TYPE_QUERY", 1); public static final NRPEPacketType TYPE_RESPONSE = new NRPEPacketType( "TYPE_RESPONSE", 2); protected NRPEPacketType(String name, int value) { super(name, value); } } 

用法:

int – > ValuedEnum:

 NRPEPacketType packetType = (NRPEPacketType) EnumUtils.getEnum(NRPEPacketType.class, 1); 

使用界面来显示谁是老板。

 public interface SleskeEnum { int id(); SleskeEnum[] getValues(); } public enum BonusType implements SleskeEnum { MONTHLY(1), YEARLY(2), ONE_OFF(3); public final int id; BonusType(int id) { this.id = id; } public SleskeEnum[] getValues() { return values(); } public int id() { return id; } } public class Utils { public static SleskeEnum getById(SleskeEnum type, int id) { for(SleskeEnum t : type.getValues()) if(t.id() == id) return t; throw new IllegalArgumentException("BonusType does not accept id " + id); } public static void main(String[] args) { BonusType shouldBeMonthly = (BonusType)getById(BonusType.MONTHLY,1); System.out.println(shouldBeMonthly == BonusType.MONTHLY); BonusType shouldBeMonthly2 = (BonusType)getById(BonusType.MONTHLY,1); System.out.println(shouldBeMonthly2 == BonusType.YEARLY); BonusType shouldBeYearly = (BonusType)getById(BonusType.MONTHLY,2); System.out.println(shouldBeYearly == BonusType.YEARLY); BonusType shouldBeOneOff = (BonusType)getById(BonusType.MONTHLY,3); System.out.println(shouldBeOneOff == BonusType.ONE_OFF); BonusType shouldException = (BonusType)getById(BonusType.MONTHLY,4); } } 

结果是:

 C:\Documents and Settings\user\My Documents>java Utils true false true true Exception in thread "main" java.lang.IllegalArgumentException: BonusType does not accept id 4 at Utils.getById(Utils.java:6) at Utils.main(Utils.java:23) C:\Documents and Settings\user\My Documents> 

你也许可以使用类似的东西

 interface EnumWithId { public int getId(); } enum Foo implements EnumWithId { ... } 

这将减less您的公用事业类的反思的需要。

反向枚举的一个非常干净的用法示例

步骤1定义一个interface EnumConverter

 public interface EnumConverter <E extends Enum<E> & EnumConverter<E>> { public String convert(); E convert(String pKey); } 

第2步

创build一个类名ReverseEnumMap

 import java.util.HashMap; import java.util.Map; public class ReverseEnumMap<V extends Enum<V> & EnumConverter<V>> { private Map<String, V> map = new HashMap<String, V>(); public ReverseEnumMap(Class<V> valueType) { for (V v : valueType.getEnumConstants()) { map.put(v.convert(), v); } } public V get(String pKey) { return map.get(pKey); } } 

第3步

转到你Enum类,并使用EnumConverter<ContentType> implement它,当然还有覆盖接口方法。 你还需要初始化一个静态的ReverseEnumMap。

 public enum ContentType implements EnumConverter<ContentType> { VIDEO("Video"), GAME("Game"), TEST("Test"), IMAGE("Image"); private static ReverseEnumMap<ContentType> map = new ReverseEnumMap<ContentType>(ContentType.class); private final String mName; ContentType(String pName) { this.mName = pName; } String value() { return this.mName; } @Override public String convert() { return this.mName; } @Override public ContentType convert(String pKey) { return map.get(pKey); } } 

步骤4

现在创build一个Communication类文件,并调用它的新方法来将Enum转换为StringString转换为Enum 。 我只是把解释的主要方法。

 public class Communication<E extends Enum<E> & EnumConverter<E>> { private final E enumSample; public Communication(E enumSample) { this.enumSample = enumSample; } public String resolveEnumToStringValue(E e) { return e.convert(); } public E resolveStringEnumConstant(String pName) { return enumSample.convert(pName); } //Should not put main method here... just for explanation purpose. public static void main(String... are) { Communication<ContentType> comm = new Communication<ContentType>(ContentType.GAME); comm.resolveEnumToStringValue(ContentType.GAME); //return Game comm.resolveStringEnumConstant("Game"); //return GAME (Enum) } } 

点击查看完整说明

在这个代码中,对于永久和密集search,有内存或进程使用,我select内存,与转换器数组作为索引。 我希望这是有帮助的

 public enum Test{ VALUE_ONE(101, "Im value one"), VALUE_TWO(215, "Im value two"); private final int number; private final byte[] desc; private final static int[] converter = new int[216]; static{ Test[] st = values(); for(int i=0;i<st.length;i++){ cv[st[i].number]=i; } } Test(int value, byte[] description) { this.number = value; this.desc = description; } public int value() { return this.number; } public byte[] description(){ return this.desc; } public static String description(int value) { return values()[converter[rps]].desc; } public static Test fromValue(int value){ return values()[converter[rps]]; } } 

似乎这个问题的答案已经过时了Java 8的发布。

  1. 不要使用序数,因为如果在JVM之外(例如数据库)持久化,序数是不稳定的。
  2. 使用键值创build静态地图相对容易。

 public enum AccessLevel { PRIVATE("private", 0), PUBLIC("public", 1), DEFAULT("default", 2); AccessLevel(final String name, final int value) { this.name = name; this.value = value; } private final String name; private final int value; public String getName() { return name; } public int getValue() { return value; } static final Map<String, AccessLevel> names = Arrays.stream(AccessLevel.values()) .collect(Collectors.toMap(AccessLevel::getName, Function.identity())); static final Map<Integer, AccessLevel> values = Arrays.stream(AccessLevel.values()) .collect(Collectors.toMap(AccessLevel::getValue, Function.identity())); public static AccessLevel fromName(final String name) { return names.get(name); } public static AccessLevel fromValue(final int value) { return values.get(value); } } 

我不确定在Java中它是否相同,但C中的枚举types也会自动映射到整数,因此您可以使用types或整数来访问它。 你有没有试过简单地访问整数呢?

真的很好的问题:-)我曾经使用类似Mr.Ferguson的解决scheme。 我们反编译的枚举看起来像这样:

 final class BonusType extends Enum { private BonusType(String s, int i, int id) { super(s, i); this.id = id; } public static BonusType[] values() { BonusType abonustype[]; int i; BonusType abonustype1[]; System.arraycopy(abonustype = ENUM$VALUES, 0, abonustype1 = new BonusType[i = abonustype.length], 0, i); return abonustype1; } public static BonusType valueOf(String s) { return (BonusType)Enum.valueOf(BonusType, s); } public static final BonusType MONTHLY; public static final BonusType YEARLY; public static final BonusType ONE_OFF; public final int id; private static final BonusType ENUM$VALUES[]; static { MONTHLY = new BonusType("MONTHLY", 0, 1); YEARLY = new BonusType("YEARLY", 1, 2); ONE_OFF = new BonusType("ONE_OFF", 2, 3); ENUM$VALUES = (new BonusType[] { MONTHLY, YEARLY, ONE_OFF }); } } 

看到这是明显的为什么ordinal()是不稳定的。 这是super(s, i); 。 我也很悲观,你可以考虑一个比你已经列举的更​​优雅的解决scheme。 毕竟所有的枚举类是任何最终的类。

为了完整起见,下面是通过任何枚举types的索引检索枚举值的通用方法。 我的目的是使该方法的外观和感觉像Enum.valueOf(Class,String) 。 Fyi,我从这里复制了这个方法。

指数相关问题(已经在这里深入讨论)仍然适用。

 /** * Returns the {@link Enum} instance for a given ordinal. * This method is the index based alternative * to {@link Enum#valueOf(Class, String)}, which * requires the name of an instance. * * @param <E> the enum type * @param type the enum class object * @param ordinal the index of the enum instance * @throws IndexOutOfBoundsException if ordinal < 0 || ordinal >= enums.length * @return the enum instance with the given ordinal */ public static <E extends Enum<E>> E valueOf(Class<E> type, int ordinal) { Preconditions.checkNotNull(type, "Type"); final E[] enums = type.getEnumConstants(); Preconditions.checkElementIndex(ordinal, enums.length, "ordinal"); return enums[ordinal]; } 

.ordinal()values()[i]都是不稳定的,因为它们依赖于枚举的顺序。 因此,如果你改变枚举的顺序或添加/删除一些你的程序会中断。

这是一个简单而有效的方法来枚举枚举和int之间的映射。

 public enum Action { ROTATE_RIGHT(0), ROTATE_LEFT(1), RIGHT(2), LEFT(3), UP(4), DOWN(5); public final int id; Action(int id) { this.id = id; } public static Action get(int id){ for (Action a: Action.values()) { if (a.id == id) return a; } throw new IllegalArgumentException("Invalid id"); } } 

应用它的string应该不难。

 Int -->String : public enum Country { US("US",0), UK("UK",2), DE("DE",1); private static Map<Integer, String> domainToCountryMapping; private String country; private int domain; private Country(String country,int domain){ this.country=country.toUpperCase(); this.domain=domain; } public String getCountry(){ return country; } public static String getCountry(String domain) { if (domainToCountryMapping == null) { initMapping(); } if(domainToCountryMapping.get(domain)!=null){ return domainToCountryMapping.get(domain); }else{ return "US"; } } private static void initMapping() { domainToCountryMapping = new HashMap<Integer, String>(); for (Country s : values()) { domainToCountryMapping.put(s.domain, s.country); } } 

我需要一些不同的东西,因为我想用一个通用的方法。 我正在阅读字节数组的枚举。 这是我想出的地方:

 public interface EnumConverter { public Number convert(); } public class ByteArrayConverter { @SuppressWarnings("unchecked") public static Enum<?> convertToEnum(byte[] values, Class<?> fieldType, NumberSystem numberSystem) throws InvalidDataException { if (values == null || values.length == 0) { final String message = "The values parameter must contain the value"; throw new IllegalArgumentException(message); } if (!dtoFieldType.isEnum()) { final String message = "dtoFieldType must be an Enum."; throw new IllegalArgumentException(message); } if (!EnumConverter.class.isAssignableFrom(fieldType)) { final String message = "fieldType must implement the EnumConverter interface."; throw new IllegalArgumentException(message); } Enum<?> result = null; Integer enumValue = (Integer) convertToType(values, Integer.class, numberSystem); // Our enum's use Integer or Byte for the value field. for (Object enumConstant : fieldType.getEnumConstants()) { Number ev = ((EnumConverter) enumConstant).convert(); if (enumValue.equals(ev)) { result = (Enum<?>) enumConstant; break; } } if (result == null) { throw new EnumConstantNotPresentException((Class<? extends Enum>) fieldType, enumValue.toString()); } return result; } public static byte[] convertEnumToBytes(Enum<?> value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException { if (!(value instanceof EnumConverter)) { final String message = "dtoFieldType must implement the EnumConverter interface."; throw new IllegalArgumentException(message); } Number enumValue = ((EnumConverter) value).convert(); byte[] result = convertToBytes(enumValue, requiredLength, numberSystem); return result; } public static Object convertToType(byte[] values, Class<?> type, NumberSystem numberSystem) throws InvalidDataException { // some logic to convert the byte array supplied by the values param to an Object. } public static byte[] convertToBytes(Object value, int requiredLength, NumberSystem numberSystem) throws InvalidDataException { // some logic to convert the Object supplied by the'value' param to a byte array. } } 

枚举的例子:

 public enum EnumIntegerMock implements EnumConverter { VALUE0(0), VALUE1(1), VALUE2(2); private final int value; private EnumIntegerMock(int value) { this.value = value; } public Integer convert() { return value; } 

}

 public enum EnumByteMock implements EnumConverter { VALUE0(0), VALUE1(1), VALUE2(2); private final byte value; private EnumByteMock(int value) { this.value = (byte) value; } public Byte convert() { return value; } } 

只是因为接受的答案不是独立的:

支持代码:

 public interface EnumWithCode<E extends Enum<E> & EnumWithCode<E>> { public Integer getCode(); E fromCode(Integer code); } public class EnumWithCodeMap<V extends Enum<V> & EnumWithCode<V>> { private final HashMap<Integer, V> _map = new HashMap<Integer, V>(); public EnumWithCodeMap(Class<V> valueType) { for( V v : valueType.getEnumConstants() ) _map.put(v.getCode(), v); } public V get(Integer num) { return _map.get(num); } } 

使用示例:

 public enum State implements EnumWithCode<State> { NOT_STARTED(0), STARTED(1), ENDED(2); private static final EnumWithCodeMap<State> map = new EnumWithCodeMap<State>( State.class); private final int code; private State(int code) { this.code = code; } @Override public Integer getCode() { return code; } @Override public State fromCode(Integer code) { return map.get(code); } }