String 값에서 Java 열거 형을 어떻게 조회 할 수 있습니까?


164

문자열 값 (또는 다른 값)에서 열거 형을 조회하고 싶습니다. 다음 코드를 시도했지만 초기화 프로그램에서 정적을 허용하지 않습니다. 간단한 방법이 있습니까?

public enum Verbosity {

    BRIEF, NORMAL, FULL;

    private static Map<String, Verbosity> stringMap = new HashMap<String, Verbosity>();

    private Verbosity() {
        stringMap.put(this.toString(), this);
    }

    public static Verbosity getVerbosity(String key) {
        return stringMap.get(key);
    }
};

IIRC는 정적 초기화가 하향식으로 수행되므로 NPE를 제공합니다 (즉, 상단의 열거 상수는 stringMap초기화에 도달하기 전에 구성됨). 일반적인 해결책은 중첩 클래스를 사용하는 것입니다.
Tom Hawtin-tackline

빠른 응답에 감사드립니다. (FWIW 나는 Sun Javadocs 가이 문제에 매우 유용한 것을 찾지 못했습니다)
peter.murray.rust

실제로는 도서관 문제보다는 언어 문제입니다. 그러나 API 문서는 언어 디자이너가 아닌 JLS보다 더 많이 읽으므로 java.lang 문서에서 이와 같은 것들이 더 두드러 질 것입니다.
Tom Hawtin-tackline

답변:


241

valueOf각 Enum에 대해 자동으로 생성되는 방법을 사용하십시오 .

Verbosity.valueOf("BRIEF") == Verbosity.BRIEF

임의의 값으로 시작 :

public static Verbosity findByAbbr(String abbr){
    for(Verbosity v : values()){
        if( v.abbr().equals(abbr)){
            return v;
        }
    }
    return null;
}

프로파일 러가 지시하는 경우 나중에 맵 구현으로 이동하십시오.

나는 그것이 모든 값을 반복한다는 것을 알고 있지만 3 개의 열거 형 값만으로는 다른 노력을 기울일 가치가 거의 없습니다.


감사 - 나는 값이 여전히 별개 알고 있다면 나는 경우 변환을 사용할 수 있습니다
peter.murray.rust

클래스 멤버 'abbr'에 직접 액세스 할 수 있으므로 "v.abbr ()"대신 "v.abbr.equals ..."를 사용할 수 있습니다.
Amio.io

7
또한 임의의 값 (두 번째 경우)의 경우 IllegalArgumentExceptionnull을 반환 하는 대신 ( , 일치하는 항목이없는 경우)를 던지는 것을 고려 하십시오 Enum.valueOf(..).
Priidu Neemre

135

당신은 가까이 있습니다. 임의의 값을 얻으려면 다음과 같이 시도하십시오.

public enum Day { 

    MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"),
    THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ;

    private final String abbreviation;

    // Reverse-lookup map for getting a day from an abbreviation
    private static final Map<String, Day> lookup = new HashMap<String, Day>();

    static {
        for (Day d : Day.values()) {
            lookup.put(d.getAbbreviation(), d);
        }
    }

    private Day(String abbreviation) {
        this.abbreviation = abbreviation;
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public static Day get(String abbreviation) {
        return lookup.get(abbreviation);
    }
}

4
클래스 로더 문제로 인해이 방법은 안정적으로 작동하지 않습니다. 나는 그것을 추천하지 않으며 조회 맵이 액세스되기 전에 준비되지 않았기 때문에 정기적으로 실패하는 것을 보았습니다. "검색"을 열거 형 외부 나 다른 클래스에 넣어야 먼저로드됩니다.
Adam Gent

7
@ 아담 겐트 (Adam Gent) :이 정확한 구조가 예로서 제시된 JLS에 대한 링크가 아래에 있습니다. 정적 초기화는 위에서 아래로 이루어집니다. docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#d5e12267
Selena

3
예, Java 8과 같은 현대 Java는 메모리 모델을 수정했습니다. 순환 참조로 인해 초기화를 포함하면 jrebel와 같은 핫 스왑 에이전트가 여전히 엉망이됩니다.
Adam Gent

@ 아담 겐트 : 흠. 나는 매일 새로운 것을 배웁니다. [부트 스트랩 싱글 톤으로 열거 형을 리팩토링하기 위해 아주 빠르게 slinking ...] 이것이 바보 같은 질문이지만 죄송하지만 이것이 주로 정적 초기화에 문제가 있습니까?
Selena

정적 코드 블록이 초기화되지 않는 문제는 보지 못했지만 2015 년 Java에서 시작한 이후 Java 8 만 사용했습니다. JMH 1.22를 사용하는 작은 벤치 마크를 사용 HashMap<>하고 열거 형을 저장 하는 데 a 를 사용했습니다 . 루프보다 40 % 이상 빠릅니다.
cbaldan

21

Java 8을 사용하면 다음과 같이 얻을 수 있습니다.

public static Verbosity findByAbbr(final String abbr){
    return Arrays.stream(values()).filter(value -> value.abbr().equals(abbr)).findFirst().orElse(null);
}

나는 돌아 가지 않고 예외를 던질 것이다 null. 그러나 그것은 또한 유스 케이스에 의존합니다
alexander

12

@ Lyle의 대답은 다소 위험하며 열거 형을 정적 내부 클래스로 만들면 특히 효과적이지 않습니다. 대신 나는 열거 형 전에 BootstrapSingleton 맵을로드하는 이와 같은 것을 사용했습니다.

편집 이 더 이상 현대의 JVM (JVM 1.6 이상)에 문제가되지 않습니다하지만 난 JRebel 문제가 여전히 있다고 생각 할 수 있지만 재검사 그것에 기회가 없었어요 .

먼저로드

   public final class BootstrapSingleton {

        // Reverse-lookup map for getting a day from an abbreviation
        public static final Map<String, Day> lookup = new HashMap<String, Day>();
   }

이제 열거 형 생성자에로드하십시오.

   public enum Day { 
        MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"),
        THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ;

        private final String abbreviation;

        private Day(String abbreviation) {
            this.abbreviation = abbreviation;
            BootstrapSingleton.lookup.put(abbreviation, this);
        }

        public String getAbbreviation() {
            return abbreviation;
        }

        public static Day get(String abbreviation) {
            return lookup.get(abbreviation);
        }
    }

내부 열거 형이있는 경우 열거 형 정의 위에 맵을 정의 할 수 있으며 이론상으로로드해야합니다.


3
"위험한"을 정의하고 내부 클래스 구현이 중요한 이유. 그것은 위에서 아래로 정적 정의 문제에 더 가깝지만 사용자 정의 값에서 "get"메소드로 Enum 인스턴스 자체에서 정적 맵을 사용하는이 질문에 대한 링크가있는 다른 관련 답변에 작업 코드가 있습니다. 열거 형 stackoverflow.com/questions/604424/lookup-enum-by-string-value-
Darrell Teague 2016 년

2
@DarrellTeague 원래 문제는 오래된 JVM (1.6 이전) 또는 다른 JVM (IBM) 또는 핫 코드 스왑 퍼 (JRebel) 때문이었습니다. 최신 JVM에서는 문제가 발생하지 않아야하므로 답변을 삭제할 수 있습니다.
Adam Gent

실제로 사용자 정의 컴파일러 구현 및 런타임 최적화로 인해 가능합니다. 순서가 다시 배열되어 오류가 발생할 수 있습니다. 그러나 이것은 사양을 위반 한 것으로 보입니다.
Darrell Teague

7

그리고 valueOf () 사용할 수 없습니다 ?

편집 : Btw, 열거 형에서 정적 {}을 사용하지 못하게하는 것은 없습니다.


또는 valueOfenum 클래스 의 합성 이므로 enum 클래스를 지정할 필요가 없습니다 Class.
Tom Hawtin-tackline

물론 javadoc 항목이 없으므로 링크하기가 어렵습니다.
Fredrik

1
valueOf ()를 사용할 수 있지만 이름은 열거 형 선언에 설정된 것과 동일해야합니다. 위의 두 가지 조회 방법 중 하나를 수정하여 .equalsIgnoringCase ()를 사용할 수 있으며 오류에 대한 견고성이 거의 없습니다.

@leonardo 맞습니다. 견고성을 추가하거나 내결함성이 있다면 논쟁의 여지가 있습니다. 대부분의 경우 후자라고 말하고이 경우 다른 곳에서 처리하고 Enum의 valueOf ()를 사용하는 것이 좋습니다.
Fredrik

4

다른 사람들에게 도움이되는 경우 여기에 나열되지 않은 선호하는 옵션은 Guava의지도 기능을 사용 합니다 .

public enum Vebosity {
    BRIEF("BRIEF"),
    NORMAL("NORMAL"),
    FULL("FULL");

    private String value;
    private Verbosity(final String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }

    private static ImmutableMap<String, Verbosity> reverseLookup = 
            Maps.uniqueIndex(Arrays.asList(Verbosity.values()), Verbosity::getValue);

    public static Verbosity fromString(final String id) {
        return reverseLookup.getOrDefault(id, NORMAL);
    }
}

기본 사용하면 사용할 수 있습니다 null수행 할 수 있습니다, throw IllegalArgumentException또는 당신이 fromString를 반환 할 수 Optional원하는대로 행동.


3

Java 8부터 정적 블록없이 한 줄로 맵을 초기화 할 수 있습니다

private static Map<String, Verbosity> stringMap = Arrays.stream(values())
                 .collect(Collectors.toMap(Enum::toString, Function.identity()));

+1 스트림 사용이 뛰어나고 가독성을 희생하지 않으면 서 매우 간결합니다! 나는 또한 Function.identity()이전의 이러한 사용을 보지 못했고 , 나는 그것보다 훨씬 더 문자 적으로 매력적이라는 것을 알았습니다 e -> e.
마쓰 Q.

0

아마도 이것 좀보세요. 그것은 나를 위해 일하고 있습니다. 이것의 목적은 '/ red_color'로 'RED'를 조회하는 것입니다. 를 선언 static map하고 enums를 한 번만 로드하면 성능 enum이 많은 경우 성능상의 이점 이 있습니다.

public class Mapper {

public enum Maps {

    COLOR_RED("/red_color", "RED");

    private final String code;
    private final String description;
    private static Map<String, String> mMap;

    private Maps(String code, String description) {
        this.code = code;
        this.description = description;
    }

    public String getCode() {
        return name();
    }

    public String getDescription() {
        return description;
    }

    public String getName() {
        return name();
    }

    public static String getColorName(String uri) {
        if (mMap == null) {
            initializeMapping();
        }
        if (mMap.containsKey(uri)) {
            return mMap.get(uri);
        }
        return null;
    }

    private static void initializeMapping() {
        mMap = new HashMap<String, String>();
        for (Maps s : Maps.values()) {
            mMap.put(s.code, s.description);
        }
    }
}
}

당신의 의견을 넣어주세요.


-1

다음 코드로 Enum을 정의 할 수 있습니다.

public enum Verbosity 
{
   BRIEF, NORMAL, FULL, ACTION_NOT_VALID;
   private int value;

   public int getValue()
   {
     return this.value;
   } 

   public static final Verbosity getVerbosityByValue(int value)
   {
     for(Verbosity verbosity : Verbosity.values())
     {
        if(verbosity.getValue() == value)
            return verbosity ;
     }

     return ACTION_NOT_VALID;
   }

   @Override
   public String toString()
   {
      return ((Integer)this.getValue()).toString();
   }
};

자세한 설명은 다음 링크를 참조하십시오


-1

기본값을 원하고 조회 맵을 작성하지 않으려는 경우이를 처리하기위한 정적 메소드를 작성할 수 있습니다. 이 예제는 또한 예상 이름이 숫자로 시작하는 조회를 처리합니다.

    public static final Verbosity lookup(String name) {
        return lookup(name, null);
    }

    public static final Verbosity lookup(String name, Verbosity dflt) {
        if (StringUtils.isBlank(name)) {
            return dflt;
        }
        if (name.matches("^\\d.*")) {
            name = "_"+name;
        }
        try {
            return Verbosity.valueOf(name);
        } catch (IllegalArgumentException e) {
            return dflt;
        }
    }

보조 값으로 필요하면 다른 답변과 마찬가지로 먼저 조회 맵을 작성하면됩니다.


-1
public enum EnumRole {

ROLE_ANONYMOUS_USER_ROLE ("anonymous user role"),
ROLE_INTERNAL ("internal role");

private String roleName;

public String getRoleName() {
    return roleName;
}

EnumRole(String roleName) {
    this.roleName = roleName;
}

public static final EnumRole getByValue(String value){
    return Arrays.stream(EnumRole.values()).filter(enumRole -> enumRole.roleName.equals(value)).findFirst().orElse(ROLE_ANONYMOUS_USER_ROLE);
}

public static void main(String[] args) {
    System.out.println(getByValue("internal role").roleName);
}

}


-2

Enum::valueOf()위의 Gareth Davis & Brad Mace이 제안한 함수를 사용할 수 있지만 IllegalArgumentException사용 된 문자열이 열거 형에 없으면 처리 될 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.