program tip

enum과 int / String 사이를 편리하게 매핑

radiobox 2020. 8. 6. 08:11
반응형

enum과 int / String 사이를 편리하게 매핑


유한 한 수의 값만 취할 수있는 변수 / 매개 변수를 다룰 때는 항상 다음 enum과 같이 Java를 사용하려고합니다 .

public enum BonusType {
  MONTHLY, YEARLY, ONE_OFF
}

내 코드 안에 머무르는 한 정상적으로 작동합니다. 그러나 종종 동일한 목적으로 일반 int(또는 String) 값을 사용하는 다른 코드와 인터페이스해야 하거나 데이터가 숫자 또는 문자열로 저장된 데이터베이스에서 읽고 쓸 필요가 있습니다.

이 경우 각 열거 형 값을 정수와 연결하여 두 가지 방법으로 변환 할 수있는 편리한 방법을 원합니다 (즉, "가역적 인 열거 형"이 필요합니다).

enum에서 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;.

그러나 int에서 enum으로 전환하는 좋은 방법은 없습니다. 이상적으로는

BonusType bt = BonusType.getById(2); 

내가 얻을 수있는 유일한 해결책은 다음과 같습니다.

  • 조회 BonusType.values()"int-> enum"을 채우는 데 사용하는 조회 메소드를 열거 형 에 넣은 다음이를 캐시하여 조회에 사용합니다. 작동하지만이 방법을 내가 사용하는 각 열거 형에 똑같이 복사해야합니다.
  • 조회 메소드를 정적 유틸리티 클래스에 넣으십시오. 그런 다음 하나의 "조회"방법 만 필요하지만 임의의 열거 형으로 작동하려면 반사로 바이올린을 조정해야합니다.

이러한 간단한 (?) 문제는 두 방법 모두 매우 어색해 보입니다.

다른 아이디어 / 통찰력이 있습니까?


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

이 솔루션은 열거 형 정의의 일부로 int 값을 사용하는 것과 유사하게 시작됩니다. 그런 다음 제네릭 기반 조회 유틸리티를 만듭니다.

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

이 솔루션은 훌륭하고 모든 리플 유형이 암시 적으로 Enum 인터페이스를 상속한다는 사실에 기반하기 때문에 '리플렉션 사용'을 필요로하지 않습니다.


enum → int

yourEnum.ordinal()

int → enum

EnumType.values()[someInt]

문자열 → 열거 형

EnumType.valueOf(yourString)

열거 형 → 문자열

yourEnum.name()

참고 :
올바르게 지적하면 ordinal()버전마다 "불안정"할 수 있습니다. 이것이 상수를 항상 데이터베이스에 문자열로 저장하는 정확한 이유입니다. (실제로 MySql을 사용할 때 MySql 열거 형 으로 저장합니다 !)


나는 이것을 웹에서 찾았는데, 그것은 매우 도움이되고 구현하기 간단했다. 이 솔루션은 내가 만든 것이 아닙니다

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

}


이 질문에 대한 답변은 Java 8 릴리스에서 구식 인 것 같습니다.

  1. 데이터베이스와 같은 JVM 외부에 지속되는 경우 서 수가 불안정하므로 서수를 사용하지 마십시오.
  2. 키 값으로 정적 맵을 작성하는 것이 비교적 쉽습니다.

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

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

각 Enum에 대한 상용구 코드 또는 중복 코드를 작성하지 않기 위해 ValuedEnum대신 Apache Commons Lang을 사용했습니다.

정의 :

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

아마도 다음과 같은 것을 사용할 수 있습니다

interface EnumWithId {
    public int getId();

}


enum Foo implements EnumWithId {

   ...
}

유틸리티 클래스에 반영해야 할 필요성이 줄어 듭니다.


이 코드에서는 영구적이고 강렬한 검색을 위해 메모리 또는 프로세스를 사용하고 변환기 배열을 인덱스로 사용하여 메모리를 선택합니다. 도움이 되길 바랍니다.

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

인터페이스를 사용하여 누가 보스인지 보여줍니다.

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>

모두 .ordinal()values()[i]그들이 열거의 순서에 의존하기 때문에 불안정하다. 따라서 열거 형의 순서를 변경하거나 일부를 추가 / 삭제하면 프로그램이 중단됩니다.

다음은 enum과 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");
    }
}

문자열에 적용하는 것은 어렵지 않습니다.


리버스 Enum의 매우 깨끗한 사용 예

1 단계interface EnumConverter 정의

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

2 단계

클래스 이름 만들기 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클래스와 implement그것과 EnumConverter<ContentType>물론 재정의 인터페이스 방법. 정적 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 단계

이제 Communication클래스 파일을 만들고 Enumto StringStringto 로 변환하는 새로운 메소드를 호출하십시오 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)
    }
}

자세한 설명을 보려면 클릭하십시오


Java에서 동일한 지 확실하지 않지만 C의 열거 형은 자동으로 정수에 매핑되므로 형식이나 정수를 사용하여 액세스 할 수 있습니다. 정수로 아직 액세스하려고 했습니까?


정말 좋은 질문 :-) 나는 얼마 전에 Ferguson과 비슷한 솔루션을 사용했습니다. 디 컴파일 된 열거 형은 다음과 같습니다.

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()불안정한 지 수 있습니다. 그것은이다 i에서 super(s, i);. 또한 이미 열거 한 것보다 더 우아한 솔루션을 생각할 수 있다고 비관적입니다. 모든 열거 형은 최종 클래스로서의 클래스입니다.


완성도를 높이기 위해 다음은 모든 열거 형 유형에서 인덱스별로 열거 형 값을 검색하는 일반적인 방법입니다. 내 의도는 메소드를 Enum.valueOf (Class, String) 처럼 보이게하고 느끼게하는 것이 었습니다 . Fyi, 나는이 방법을 here 에서 복사했습니다 .

색인 관련 문제 (이미 자세히 설명되어 있음)가 여전히 적용됩니다.

/**
 * 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];
}

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

}

주어진:

공개 열거 형 BonusType {MONTHLY (0), YEARLY (1), ONE_OFF (2)}

BonusType 보너스 = 매년;

System.out.println (bonus.Ordinal () + ":"+ 보너스)

산출 : 1 : YEARLY

참고 URL : https://stackoverflow.com/questions/5021246/conveniently-map-between-enum-and-int-string

반응형