String値からJava enumを検索するにはどうすればよいですか?


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初期化に至る前に、上部の列挙型定数が構築されます)。通常の解決策は、ネストされたクラスを使用することです。
トム・ホーティン-2009

迅速な対応ありがとうございました。(FWIW私は、Sun Javadocsがこの問題にあまり役立つとは思いませんでした)。
peter.murray.rust 2009

ライブラリの問題というよりは、言語の問題です。ただし、APIドキュメントはJLSよりも(おそらく言語デザイナーではなく)読まれていると思います。そのため、このようなものは、おそらくjava.langドキュメントでより目立つはずです。
トム・ホーティン-2009

回答:


241

valueOfEnumごとに自動的に作成されるメソッドを使用します。

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

任意の値の場合、次で始まります:

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

プロファイラーから指示された場合のみ、後でMapの実装に進んでください。

私はそれがすべての値を反復していることを知っていますが、列挙値が3つしかないので、他の努力をする価値はほとんどありません。


おかげで-私は値がまだ明瞭である知っていれば、私は小文字の変換を使用することができます
peter.murray.rust

クラスメンバー「abbr」に直接アクセスできるため、「v.abbr()」の代わりに「v.abbr.equals ...」を使用できます。
Amio.io 2015

7
また、任意の値(2番目のケース)の場合は、IllegalArgumentExceptionnullを返す代わりに(つまり、一致が見つからない場合)をスローすることを検討Enum.valueOf(..)してください。これが、JDKが行う方法です。
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
クラスローダーの問題により、このメソッドは信頼性の高い動作をしません。アクセスする前にルックアップマップの準備ができていないため、推奨しません。「ルックアップ」を列挙型の外側または別のクラスに配置して、最初にロードされるようにする必要があります。
アダム・ゲント

7
@Adam Gent:下にJLSへのリンクがあり、この正確な構成が例として示されています。静的な初期化が上から下に発生すると言われています:docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#d5e12267
Selena

3
はい、Java 8のような最新のJavaはメモリモデルを修正しました。ただし、循環参照のために初期化を埋め込むと、jrebelなどのホットスワップエージェントは引き続き混乱します。
Adam Gent

@Adam Gent:うーん。私は毎日新しいことを学びます。[ブートストラップシングルトンを使用して列挙型をリファクタリングするために静かに急降下する...]これが愚かな質問である場合は申し訳ありませんが、これは主に静的な初期化に関する問題ですか?
セレナ

私は2015年に、私はちょうどJMH 1.22を使用して、ほとんどベンチマークを行なったし、使用して、初期化されているが、私はJavaで始めて以来、私は唯一のJava 8を使用していない静的コードブロックの問題を見たことがないHashMap<>ことを証明した列挙型を格納しますfoorループより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。しかし、それはまた、ユースケースに応じている
アレクサンダー

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
「危険」を定義する必要があり、内部クラスの実装が重要である理由。それは上から下への静的定義の問題ですが、ユーザー定義の値から「取得」メソッドを使用してEnumインスタンス自体に静的マップを使用するこの質問へのリンクを持つ別の関連する回答に作業コードがあります列挙型。stackoverflow.com/questions/604424/lookup-enum-by-string-value
Darrell Teague

2
@DarrellTeague元の問題は、古いJVM(1.6より前)または他のJVM(IBM)またはホットコードスワッパー(JRebel)が原因でした。この問題は最新のJVMでは発生しないはずなので、答えを削除してもかまいません。
Adam Gent

カスタムコンパイラの実装とランタイムの最適化が原因で実際​​にそれが可能であることに注意してください...順序が並べ替えられ、エラーが発生する可能性があります。ただし、これは仕様に違反しているように見えます。
ダレルティーグ

7

そして、あなたはvalueOf()を使用することはできませんか?

編集:ところで、列挙型で静的{}を使用することを妨げるものは何もありません。


または、valueOfenumクラスの合成なので、enumクラスを指定する必要はありませんClass
トム・ホーティン-2009

もちろん、javadocエントリがなかっただけなので、リンクするのは困難でした。
フレドリック、

1
valueOf()を使用できますが、名前はenum宣言で設定された名前と完全に一致する必要があります。上記の2つのルックアップメソッドのいずれかを変更して、.equalsIgnoringCase()を使用し、エラーに対する堅牢性を高めることができます。

@leonardoそうです。堅牢性を追加するか、フォールトトレランスだけにするかは議論の余地があります。ほとんどの場合、後者です。その場合、他の場所で処理し、EnumのvalueOf()を使用することをお勧めします。
Fredrik

4

他の人を助けるために、ここにリストされていない私が好むオプションは、グアバのマップ機能を使用しています

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ますfromStringOptional、好きな動作をすることができます。


3

Java 8以降では、静的ブロックなしで1行でマップを初期化できます

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 mapenumsを1回だけロードすると、enumsが多い場合にパフォーマンスが向上します。

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.