Java numaralandırmasını Dize değerinden nasıl arayabilirim?


164

Ben bir enum dize değeri (veya muhtemelen başka bir değer) aramak istiyorum. Aşağıdaki kodu denedim ama initialisers statik izin vermez. Basit bir yolu var mı?

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

Statik başlatma yukarıdan aşağıya yapıldığından NPE veren IIRC (yani, enum sabitleri stringMapbaşlatma aşamasına geçmeden önce oluşturulur ). Genel çözüm, iç içe bir sınıf kullanmaktır.
Tom Hawtin - tackline

Bu kadar hızlı yanıt için herkese teşekkürler. (FWIW Sun Javadocs'u bu sorun için çok yararlı bulamadım).
peter.murray.rust

Bu gerçekten bir kütüphane sorunu olmaktan çok bir dil sorunu. Bununla birlikte, API belgelerinin JLS'den (belki de dil tasarımcıları tarafından olmasa da) daha fazla okunduğunu düşünüyorum, bu yüzden böyle şeyler muhtemelen java.lang belgelerinde daha fazla öneme sahip olmalıdır.
Tom Hawtin - tackline

Yanıtlar:


241

valueOfHer Numaralandırma için otomatik olarak oluşturulan yöntemi kullanın .

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

Rasgele değerler için şununla başlayın:

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

Daha sonra Harita uygulamasına yalnızca profil oluşturucunuz size bildirirse devam edin.

Tüm değerler üzerinde yinelediğini biliyorum, ama sadece 3 enum değeriyle başka bir çabaya değmez, aslında çok fazla değere sahip olmadıkça bir Harita ile uğraşmazdım yeterince hızlı olacak.


teşekkürler - ve değerlerin hala farklı olduğunu bildiğim durumda dönüştürme kullanabilirim
peter.murray.rust

"V.abbr ()" yerine "v.abbr.equals ..." kullanabilirsiniz. "Abbr" sınıf üyesine doğrudan erişebilirsiniz.
Amio.io

7
Ayrıca, rasgele değerler için (ikinci durum) IllegalArgumentExceptionnull döndürmek yerine ( yani eşleşme bulunmadığında) atmayı düşünün - JDK Enum.valueOf(..)bunu yine de böyle yapar.
Priidu Neemre

135

Yakınsın. Rasgele değerler için aşağıdakine benzer bir şey deneyin:

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
Sınıf yükleyici sorunları nedeniyle bu yöntem güvenilir çalışmaz. Bunu önermiyorum ve arama haritasına erişilmeden önce hazır olmadığı için düzenli olarak başarısız olduğunu gördüm. İlk olarak yüklenebilmesi için "aramayı" numaralandırmanın dışına veya başka bir sınıfa koymanız gerekir.
Adam Gent

7
@Adam Gent: Aşağıda, bu yapının örnek olarak sunulduğu JLS'ye bir bağlantı var. Statik başlatmanın yukarıdan aşağıya doğru gerçekleştiğini söylüyor: docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#d5e12267
Selena

3
Evet java 8 gibi modern java hafıza modelini düzeltti. Jrebel gibi hot swap ajanlarının dairesel referanslar nedeniyle başlatmayı gömdüğünüzde hala dağınık olduğu söyleniyor.
Adam Gent

@Adam Gent: Hmmm. Her gün yeni bir şey öğreniyorum. [Sessizce benim bootstrap singletons ile enacac refactor için slinking ...] Bu aptalca bir soru, ama bu çoğunlukla statik başlatma ile ilgili bir sorun mu üzgünüm?
Selena

Statik kod bloğunun başlatılmadığını hiç görmedim, ancak 2015'te Java'da başladığımdan beri sadece Java 8 kullandım. JMH 1.22'yi kullanarak HashMap<>ve numaralandırmaları saklamak için bir kullanarak küçük bir karşılaştırma yaptım . Bir foop halkasından% 40 daha hızlı.
cbaldan

21

Java 8 ile şu şekilde başarabilirsiniz:

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

Dönmek yerine bir istisna fırlatmak istiyorum null. Ama aynı zamanda kullanım durumuna da bağlı
alexander

12

@ Lyle'ın cevabı oldukça tehlikeli ve özellikle enum'u statik bir iç sınıf yaparsanız işe yaramadığını gördüm. Bunun yerine numaralandırmalardan önce BootstrapSingleton haritalarını yükleyecek böyle bir şey kullandım.

Düzen bu artık çağdaş JVM'lerle (JVM 1.6 veya üzeri) ile ilgili bir sorun olmamalı ama JRebel ile ilgili sorunlar hala vardır sizce ama tekrar test buna bir şansım olmadı .

Önce beni yükle:

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

Şimdi enum yapıcısına yükleyin:

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

Eğer bir iç enumunuz varsa, sadece enum tanımının üstündeki haritayı tanımlayabilirsiniz ve (teoride) daha önce yüklenmelidir.


3
"Tehlikeli" ve bir iç sınıf uygulamasının neden önemli olduğunu tanımlamak gerekir. Bu, yukarıdan aşağıya statik bir tanım sorunudur, ancak Enum örneğinde, kullanıcı tanımlı değerden "get" yöntemiyle statik bir Harita kullanan bu soruya geri bağlantı veren başka bir ilgili yanıtta çalışma kodu vardır. Numaralandırma türü. stackoverflow.com/questions/604424/lookup-enum-by-string-value
Darrell Teague

2
Orijinal sorun eski JVM'ler (1.6 öncesi) veya diğer JVM'ler (IBM'ler) veya hotcode swappers (JRebel) kaynaklanıyordu. Sorun modern JVM'lerde olmamalı, bu yüzden cevabımı silebilirim.
Adam Gent

Özel derleyici uygulamaları ve çalışma zamanı optimizasyonları nedeniyle gerçekten mümkün olduğunu unutmayın ... sipariş yeniden sonuçlandırılabilir ve bir hataya neden olabilir. Ancak, bu şartnameyi ihlal ediyor gibi görünüyor.
Darrell Teague

7

Ve valueOf () kullanamazsınız ?

Düzenleme: Btw, bir numaralandırma statik {} kullanarak sizi durduran bir şey yok.


Veya valueOfenum sınıfındaki sentetik , bu yüzden enum sınıfını belirtmenize gerek yoktur Class.
Tom Hawtin - tackline

Tabii ki, sadece bir javadoc girişi yoktu, bu yüzden bağlantı zor oldu.
Fredrik

1
ValueOf () öğesini kullanabilirsiniz, ancak adın numaralandırma bildiriminde ayarlananla aynı olması gerekir. Yukarıdaki iki arama yönteminden her ikisi de .equalsIgnoringCase () kullanmak üzere değiştirilebilir ve hataya karşı daha az sağlamlığa sahiptir.

@leonardo Doğru. Sağlamlık katıyorsa veya sadece hataya dayanıklılık tartışmalıdır. Çoğu durumda, ikincisi olduğunu söyleyebilirim ve bu durumda başka bir yerde işlemek ve hala Enum'un valueOf () yöntemini kullanmak daha iyidir.
Fredrik

4

Başkalarına yardımcı olması durumunda, burada listelenmeyen tercih ettiğim seçenek Guava'nın Haritalar işlevini kullanır :

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

Kullanabileceğiniz varsayılan değerle null, tercih ettiğiniz davranış ne olursa olsun bir throw IllegalArgumentExceptionveya fromStringgeri dönebilirsiniz Optional.


3

java 8'den beri haritayı tek bir satırda ve statik blok olmadan başlatabilirsiniz

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

+1 Akışların mükemmel kullanımı ve okunabilirlikten ödün vermeden çok özlü! Daha Function.identity()önce bu kullanımı da görmemiştim , metinsel olarak daha çekici buluyorum e -> e.
Matsu S.

0

Belki de şuna bir bakın. Benim için çalışıyor. Bunun amacı '/ red_color' ile 'KIRMIZI' aramaktır. A'nın bildirilmesi static mapve enumyalnızca bir kez yüklenmesi, enumçok sayıda olması durumunda bazı performans avantajları getirecektir .

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

Lütfen opinons'unuzu koyun.


-1

Enum'unuzu aşağıdaki kod olarak tanımlayabilirsiniz:

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

Daha fazla açıklama için aşağıdaki bağlantıya bakın


-1

Varsayılan bir değer istiyorsanız ve arama haritaları oluşturmak istemiyorsanız, bunu işlemek için statik bir yöntem oluşturabilirsiniz. Bu örnek ayrıca, beklenen adın bir sayı ile başlayacağı aramaları da ele alır.

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

İkincil bir değere ihtiyacınız varsa, önce diğer bazı cevaplarda olduğu gibi arama haritasını oluşturursunuz.


-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()Yukarıdaki Gareth Davis ve Brad Mace tarafından önerilen işlevi kullanabilirsiniz , ancak IllegalArgumentExceptionkullanılan dize numarada mevcut değilse atılacak olanı ele aldığınızdan emin olun .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.