Java'da Int numaralandırması


334

Aşağıdaki numaralandırma göz önüne alındığında, Java'da bir numaraya Int yerleştirmenin doğru yolu nedir?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


MyEnum enumValue = (MyEnum) x; //Doesn't work???

@RyuS. Bu, bu sorunun tekrarı değil,
Mark Rotteveel

Yanıtlar:


587

Deneyin MyEnum.values()[x]nerede xolmalı 0ya 1o enum için geçerli bir sıra, ie.

Java numaralandırmalarında aslında sınıflar (ve dolayısıyla numaralandırma değerleri nesnelerdir) ve dolayısıyla bir numaralandırmaya intveya hatta Integernumaralandırmaya izin veremeyeceğinizi unutmayın .


117
+1: MyEnum.values()Pahalı olduğu için önbelleklemek isteyebilirsiniz . yani yüzlerce kez diyorsan.
Peter Lawrey

6
@PeterLawrey Sadece bütünlük için neden yavaş olması gerektiğini açıklayabilir misiniz? Bunun açık bir nedeni görmüyorum.
Tarrasch

48
@Tarrasch, diziler değiştirilebilir olduğundan, değerleri () değiştirebilmeniz durumunda öğeler dizisinin bir kopyasını döndürmelidir. Bu kopyayı her seferinde oluşturmak nispeten pahalıdır.
Peter Lawrey

9
@PeterLawrey Son zamanlarda çok haskell kullanıyorum (her şeyin değişmez olduğu yerlerde)! Açık ve özlü açıklamanız için teşekkür ederiz. :)
Tarrasch

'x' nasıl alınır?
Beeing Jk

161

MyEnum.values()[x]pahalı bir işlemdir. Performans bir endişe kaynağıysa, böyle bir şey yapmak isteyebilirsiniz:

public enum MyEnum {
    EnumValue1,
    EnumValue2;

    public static MyEnum fromInteger(int x) {
        switch(x) {
        case 0:
            return EnumValue1;
        case 1:
            return EnumValue2;
        }
        return null;
    }
}

44
Anahtar bakımından kaçınmak isterseniz, using sınıfında: private final MyEnum [] myEnumValues ​​= MyEnum.values ​​(); Sonra kullanım: myEnum = myEnumValues ​​[i];
Gili Nachum

23
@GiliNachum bunu garip bir şekilde söyledi, ancak bu çözümle ilgili sorun sürdürülebilirlik. DRY prensibine aykırıdır; bu, numaralandırma değerlerinin her değiştiğinde (yeniden sıralama, katma değer (ler), katma değer (ler)) eşzamanlı olarak güncellenmesi gerektiği anlamına gelir. Gili'nin yorumu Java'yı numaralandırma değerleri listesiyle tutarlılığı sürdürmeye zorlar, numaralandırma değerlerindeki değişiklikler bu yaklaşımı hiç etkilemez.
Dandre Allison

3
@LorenzoPolidori, Neden MyEnum.values()[x]pahalı bir ameliyat olarak gördüğünüzü açıklayabilir misiniz ? Nasıl çalıştığını ayrıntılı olarak bilmiyorum, ama bana göre bir Array'deki bir öğeye erişmek önemli bir şey değil, sabit bir zaman gibi görünüyor. Dizinin oluşturulması gerekiyorsa, çözümünüzle aynı çalışma süresi olan O (n) zamanı alır.
brunsgaard

1
@brunsgaard values()Her seferinde yeni bir dizi oluşturduğunu varsayıyorum , çünkü diziler değiştirilebilir, bu yüzden aynı diziyi birden çok kez döndürmek güvenli olmazdı. Anahtar ifadeleri mutlaka O (n) değildir, tabloları atlamak için derlenebilirler. Yani Lorenzo'nun iddiaları haklı görünüyor.
MikeFHay

1
@Johnson Gili'nin sürümü, Numaralandırma bildirimindeki değişiklikler dışında herhangi bir ek kod değişikliği gerektirmez. Numaralandırmaya yeni bir öğe eklerseniz, numaralandırmadaki öğeyi myEnum = myEnumValues[i]yine de değiştirmeden döndürür i.
Dandre Allison

45

Tamsayı değerlerinizi vermek istiyorsanız, aşağıdaki gibi bir yapı kullanabilirsiniz

public enum A
{
        B(0),
        C(10),
        None(11);
        int id;
        private A(int i){id = i;}

        public int GetID(){return id;}
        public boolean IsEmpty(){return this.equals(A.None);}
        public boolean Compare(int i){return id == i;}
        public static A GetValue(int _id)
        {
            A[] As = A.values();
            for(int i = 0; i < As.length; i++)
            {
                if(As[i].Compare(_id))
                    return As[i];
            }
            return A.None;
        }
}

5
+1, çünkü değerlerin ardışık olması gerekmediği gerçeğini vurgular.
rhavin

Ayrıca, 0'dan varsayılan sıra sayıları kullanmak yerine seyrek (veya tekrarlanan) bir tamsayı değerleri kümesi istiyorsanız işe yarayan tek yanıt budur. (Sayı-1). Ağ üzerinden var olan bir kodla etkileşime giriyorsanız bu önemli olabilir.
benkc

Yukarıdaki yanıtlarda olduğu gibi, her çağrışınızda bir bellek ayırma ve dizilimden kaçınmak için değerlerin () sonucunun önbelleğe alınmasının muhtemelen faydalı olduğuna dikkat çekmeye değer.
benkc

1
Numaralandırmanızın uzunluğuna bağlı olarak, her seferinde doğrusal bir arama yapmak yerine bir HashMap oluşturmak veya bu arama için bir ikili arama veya başka bir şey kullanmak isteyebilirsiniz.
benkc

2
Bu int enum dönüştürmek için doğru yolu ve en iyi uygulama olmalı ve ben kamu statik A GetValue (int _id) {için (A a: A.values ​​() {if (a.getId ( ) == _ id) {return a;}} return null;} Hiçbiri'nden kurtulun, isEmpty () ve karşılaştırmak () şeyler.
Chris.Zou

35

Bunu deneyebilirsin.
Öğe kimliğiyle Sınıf oluşturun.

      public Enum MyEnum {
        THIS(5),
        THAT(16),
        THE_OTHER(35);

        private int id; // Could be other data type besides int
        private MyEnum(int id) {
            this.id = id;
        }

        public static MyEnum fromId(int id) {
                for (MyEnum type : values()) {
                    if (type.getId() == id) {
                        return type;
                    }
                }
                return null;
            }
      }

Şimdi int olarak id kullanarak bu Numaralamayı getir.

MyEnum myEnum = MyEnum.fromId(5);

19

Ben değerleri önbellek ve basit bir statik erişim yöntemi oluşturmak:

public static enum EnumAttributeType {
    ENUM_1,
    ENUM_2;
    private static EnumAttributeType[] values = null;
    public static EnumAttributeType fromInt(int i) {
        if(EnumAttributeType.values == null) {
            EnumAttributeType.values = EnumAttributeType.values();
        }
        return EnumAttributeType.values[i];
    }
}

4
Şimdi kullandığım çözüm bu. Ancak IMHO, alana valuesyöntemle aynı adı vermezseniz daha az kafa karıştırıcıdır values(). cachedValuesAlan adı için kullanıyorum .
ToolmakerSteve

2
Verimli ve zarif; kopyalandım ve projeme yapıştırdım :) Değiştirdiğim tek şey, sadece int (int i) den çağrıldığım fromInt (int i) 'dir.
pipedreambomb

1
neden başlangıçtan başlamıyorsunuz? neden ilk vuruşu bekliyorsun?
kaiser

9

Java numaralandırmalarında, C ++ ile aynı numaralandırma-int eşlemesi yoktur.

Bununla birlikte, tüm valuesnumaralandırmaların bir dizi olası numaralandırma değeri döndüren bir yöntemi vardır,

MyEnum enumValue = MyEnum.values()[x];

çalışmalı. Biraz pis ve o denemek ve dönüştürürler değil daha iyi olabilir intiçin s Enummümkünse (tersi veya yardımcısı) s.


9

Bu genellikle yapılan bir şey değil, bu yüzden tekrar düşünürdüm. Ancak bunu söyledikten sonra, temel işlemler şunlardır: EnumType.values ​​() [intNum] kullanarak int -> enum ve enumInst.ordinal () kullanarak enum -> int.

Ancak, değerlerin () herhangi bir uygulamasının size dizinin bir kopyasını vermekten başka seçeneği olmadığı için (java dizileri hiçbir zaman salt okunur değildir), enum -> int eşlemesini önbelleğe almak için bir EnumMap kullanarak daha iyi sunulur.


2
Re "Bu genellikle yapılan bir şey değil": Yararlı olduğu yaygın durum: enum bir veritabanında depolanan int değerlerine karşılık gelir.
ToolmakerSteve

Eşlemelerin zorunlu olduğu konusunda kesinlikle haklısınız. Ancak bu tür bir kodlamayı bazı OR eşleştiricisine veya bazı araç setine / kitaplığa bırakmak ister misiniz?
Dilum Ranatunga


6

İşte planladığım çözüm. Bu yalnızca sıralı olmayan tamsayılarla çalışmaz, aynı zamanda numaralandırma değerleriniz için temel kimlik olarak kullanmak isteyebileceğiniz diğer veri türleriyle de çalışmalıdır.

public Enum MyEnum {
    THIS(5),
    THAT(16),
    THE_OTHER(35);

    private int id; // Could be other data type besides int
    private MyEnum(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    public static Map<Integer, MyEnum> buildMap() {
        Map<Integer, MyEnum> map = new HashMap<Integer, MyEnum>();
        MyEnum[] values = MyEnum.values();
        for (MyEnum value : values) {
            map.put(value.getId(), value);
        }

        return map;
    }
}

Ben sadece bir dosyadan veri yüklerken kimlikleri belirli zamanlarda numaralarını dönüştürmek gerekir, bu yüzden Harita her zaman bellekte tutmak için hiçbir neden yoktur. Haritanın her zaman erişilebilir olması gerekiyorsa, haritayı her zaman Enum sınıfınızın statik bir üyesi olarak önbelleğe alabilirsiniz.


IMHO bellek kullanımı konusunda endişeliysem, HashMap'i dinamik olarak, @ossys gibi, ancak önbellek null olduğunda farklı bir kodla yaratacağım, daha sonra clearCachedValues(özel alanı null değerine geri ayarlar) kullandığınızda ikinci bir yöntem ekleyin . Ben düşünün MyEnum.fromInt(i)bir harita nesnesi etrafında geçen daha anlamak daha kolay.
ToolmakerSteve

6

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

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

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

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

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
}

Varsayılan ile kullanabilirsiniz nullyapabilirsiniz, throw IllegalArgumentExceptionveya senin fromIntbir geri dönebilirler OptionalTercih ettiğiniz herhangi davranış.


3
Guava kullandığınızı belirtmelisiniz. Veya akışları kullanabilirsiniz:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel

1
Bir getValue()yöntem de tanımlamak isteyebilir .
shmosel

@shmosel Hata, getValue işlevini kaçırdım, teşekkürler. Stackoverflow içine genel sürümleri yazmak her zaman dışarı kaydırmaz. Guava'yı bir bağlantıyla kullanma hakkında yorum eklendi. Akışlara Guava yöntemini tercih edin.
Chad Befus

4

values()Enum üzerinde yineleme yapabilir ve enum'un tamsayı değerini idaşağıdaki gibi verebilirsiniz :

public enum  TestEnum {
    None(0),
    Value1(1),
    Value2(2),
    Value3(3),
    Value4(4),
    Value5(5);

    private final int value;
    private TestEnum(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static TestEnum  getEnum(int value){
        for (TestEnum e:TestEnum.values()) {
            if(e.getValue() == value)
                return e;
        }
        return TestEnum.None;//For values out of enum scope
    }
}

Ve aynen böyle kullanın:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
Umarım bu yardımcı olur;)


3

@ChadBefus'un cevabına ve @shmosel yorumuna dayanarak bunu kullanmanızı tavsiye ederim. (Verimli arama ve saf java üzerinde çalışır> = 8)

import java.util.stream.Collectors;
import java.util.function.Function;
import java.util.Map;
import java.util.Arrays;

public enum MyEnum {
    OPTION_1(-66),
    OPTION_2(32);

    private int value;
    private MyEnum(final int value) {
        this.value = value;
    }

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

    private static Map<Integer, MyEnum> reverseLookup =
        Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));

    public static MyEnum fromInt(final int id) {
        return reverseLookup.getOrDefault(id, OPTION_1);
    }
    public static void main(String[] args) {
        System.out.println(fromInt(-66).toString());
    }
}

0

İyi bir seçenek olan önlemek dönüşüm intiçin enum: Eğer azami değer gerekirse, örneğin, sen x.ordinal karşılaştırabilirsiniz () y.ordinal () ve geri dönüş x veya y uygun. (Bu karşılaştırmayı anlamlı hale getirmek için değerlerinizi yeniden sıralamanız gerekebilir.)

Bu mümkün değilse, MyEnum.values()statik bir dizi saklamak istiyorum .


1
Eğer DB int almak ve bunu çok yaygın bir görev olduğunu düşünüyorum Enum dönüştürmek istiyorsanız, int -> enum dönüşüm, IMO gerekir.
Yuki Inoue

0

Bu, doktorlarla aynı cevaptır, ancak değişebilir dizilerdeki sorunun nasıl ortadan kaldırılacağını gösterir. Şube tahmininden dolayı bu tür bir yaklaşımı kullanırsanız, ilk önce çok azdan sıfıra etkisi olur ve tüm kod yalnızca bir kez değişebilir dizi değerleri () işlevini çağırır. Her iki değişken de statik olduğundan, bu numaralandırmanın her kullanımı için n * bellek tüketmezler.

private static boolean arrayCreated = false;
private static RFMsgType[] ArrayOfValues;

public static RFMsgType GetMsgTypeFromValue(int MessageID) {
    if (arrayCreated == false) {
        ArrayOfValues = RFMsgType.values();
    }

    for (int i = 0; i < ArrayOfValues.length; i++) {
        if (ArrayOfValues[i].MessageIDValue == MessageID) {
            return ArrayOfValues[i];
        }
    }
    return RFMsgType.UNKNOWN;
}

0
enum MyEnum {
    A(0),
    B(1);
    private final int value;
    private MyEnum(int val) {this.value = value;}
    private static final MyEnum[] values = MyEnum.values();//cache for optimization
    public static final getMyEnum(int value) { 
        try {
            return values[value];//OOB might get triggered
        } catch (ArrayOutOfBoundsException e) {
        } finally {
            return myDefaultEnumValue;
        }
    }
}


@shmosel Sınırların dışında göreceli olarak çok az olması durumunda istisnaların çok daha iyi olduğuna inanıyorum.
Zoso

İnancınız neye dayanıyor?
shmosel

0

Kotlin'de:

enum class Status(val id: Int) {
    NEW(0), VISIT(1), IN_WORK(2), FINISHED(3), CANCELLED(4), DUMMY(5);

    companion object {
        private val statuses = Status.values().associateBy(Status::id)

        fun getStatus(id: Int): Status? = statuses[id]
    }
}

Kullanımı:

val status = Status.getStatus(1)!!

0

Bu uygulamayı yazdı. Eksik değerlere, negatif değerlere izin verir ve kodu tutarlı tutar. Harita da önbelleğe alınır. Bir arayüz kullanır ve Java 8 gerektirir.

Sıralama

public enum Command implements OrdinalEnum{
    PRINT_FOO(-7),
    PRINT_BAR(6),
    PRINT_BAZ(4);

    private int val;
    private Command(int val){
        this.val = val;
    }

    public int getVal(){
        return val;
    }

    private static Map<Integer, Command> map = OrdinalEnum.getValues(Command.class);
    public static Command from(int i){
        return map.get(i);
    }
}

Arayüz

public interface OrdinalEnum{
    public int getVal();

    @SuppressWarnings("unchecked")
    static <E extends Enum<E>> Map<Integer, E> getValues(Class<E> clzz){
        Map<Integer, E> m = new HashMap<>();
        for(Enum<E> e : EnumSet.allOf(clzz))
            m.put(((OrdinalEnum)e).getVal(), (E)e);

        return m;
    }
}
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.