Enum ordinal biriminden enum tipine dönüştürme


316

ReportTypeEnumTüm sınıflarımda yöntemler arasında geçen enum türü ettik ama sonra int değeri almak için ordinal yöntemi kullanmak bu yüzden URL bu geçmek gerekir. Diğer JSP sayfama aldıktan sonra, onu ReportTypeEnumgeçirmeye devam edebilmem için bire geri dönüştürmem gerekiyor.

Ordinal'i nasıl dönüştürebilirim ReportTypeEnum?

Java 6 SE kullanma.


1
Şimdiye kadar Java 6 EE yok (AFAIK). Java SE 6 ve Java EE 5 var.
Hosam Aly

Yanıtlar:


632

Bir ordinali enum temsiline dönüştürmek için bunu yapmak isteyebilirsiniz:

ReportTypeEnum value = ReportTypeEnum.values()[ordinal];

Lütfen dizi sınırlarına dikkat edin.

Her arama çağrısının, values()performansı olumsuz yönde etkileyebilecek yeni klonlanmış bir dizi döndürdüğünü unutmayın . Diziyi sık sık arayacaksanız önbelleğe almak isteyebilirsiniz.

Nasıl önbelleğe alınacağına ilişkin kod örneğivalues() .


Bu cevap, yorumlara verilen geri bildirimi içerecek şekilde düzenlendi


Bu çözümü uyguladım ve benim için çalışmıyor. Sıralı değerin, numaralandırılmış türlerin eklendiği sırayla eşleşeceği garanti edilmez. Bu cevabın ne olduğunu bilmiyorum, ama yine de insanları uyarmak istedim
IcedDante

@IcesDante: ordinalın kaynaktaki Numaralandırma değerlerinin sırasına karşılık gelmesi kesinlikle garanti edilir. Farklı bir davranış gözlemlerseniz, başka bir şey yanlış olmalıdır. Ancak yukarıdaki cevabım, diğer cevaplarda ortaya konan tüm nedenlerden dolayı yetersizdir.
Joachim Sauer

@JoachimSauer Belki IcedDante, sıra değerinin farklı bir sırada olduğu kaynağın önceki bir sürümü tarafından üretilip kaydedildiyse ordinalin eşleşmeyebileceği anlamına gelir.
LarsH

137

Bu neredeyse kesinlikle kötü bir fikir . Kesinlikle ordinal fiili kalıcı ise (örneğin, birisi URL'ye yer işareti enumkoyduğu için) - bu , sıradaki kod koruyucular tarafından açıkça görülmeyen, gelecekte siparişi her zaman korumanız gerektiği anlamına gelir .

Neden kodlamak değil enumkullanarak myEnumValue.name()(ve kod çözme yoluyla ReportTypeEnum.valueOf(s)) yerine?


24
Numaralandırma adını değiştirirseniz (ancak siparişi korursanız) ne olur?
Arne Evertsson

6
@Arne - Bu, deneyimsiz bir insanın gelmesinden ve valuebaşlangıçta veya doğru alfabetik / mantıksal pozisyonda eklemesinden çok daha az olası olduğunu düşünüyorum . (Mantıksal olarak demek istediğim, örneğin TimeUnitdeğerlerin mantıksal bir konumu vardır)
oxbow_lakes

7
Kesinlikle benim numaralandırma yerine numaralandırma sipariş zorlamak için tercih ... Bu yüzden veritabanında numaralandırma adı yerine sıra sıra saklamayı tercih ediyorum. Ayrıca, String yerine int manipülasyonu kullanmak daha iyidir ...

8
Katılıyorum. Herkese açık bir API'da, bir Enum'un adını değiştirmek geriye dönük uyumluluğu bozar, ancak siparişi değiştirmek olmaz. Bu nedenle, adı "anahtar" olarak kullanmak daha mantıklıdır
Noel

3
Sıralamayı saklamak fikirlerinizi diğer dillere çevirmenizi kolaylaştırır. C'de bir bileşen yazmanız gerekirse ne olur?
QED

94

Çok kullanacaksam values():

enum Suit {
   Hearts, Diamonds, Spades, Clubs;
   public static final Suit values[] = values();
}

Bu arada her yerde. Java:

Suit suit = Suit.values[ordinal];

Dizi sınırlarınıza dikkat edin.


3
+1 bu en iyi çözüm IMHO çünkü biri özellikle android.os.Message içinde ordinals geçebilir.
likejudo

9
Bu çok endişe vericidir çünkü diziler değiştirilebilir . [] Değerleri kesin olsa da Suit.values[0] = Suit.Diamonds;, kodunuzda bir yeri engellemez . İdeal olarak bu asla gerçekleşmeyecektir, ancak değişebilir alanları ortaya çıkarma ilkesi hala geçerlidir. Bu yaklaşım Collections.unmodifiableListiçin bunun yerine veya benzerlerini kullanmayı düşünün .
Mshnik

Nasıl olur - özel statik son Suit değerleri [] = değerler (); public static Suit [] getValues ​​() {dönüş değerleri; }
Pratyush

4
@Pratyush, dizi değişkenini değiştirilemez hale getirir, ancak içeriğini değiştirmez. Yine de getValues ​​() [0] = bir şey yapabilirdim;
Calabacin

Katılıyorum, bu nedenle nokta Suit values[]doğrudan veya dolaylı olarak (nasıl geçildi) ortaya değil getValues(), ordinaldeğerin nasıl bir argüman gönderilmesi ve Suittemsili geri döndürülmesi gereken bir genel yöntem ile çalışıyorum Suit values[]. Buradaki nokta (başından itibaren sorunun çini) bir numara sıralamasından bir numara türü oluşturmaktı
Manuel Jordan

13

Çoğu insanla ordinal kullanmanın muhtemelen kötü bir fikir olduğunu kabul ediyorum. Ben genellikle enum örneğin bir DB değeri alabilir özel bir yapıcı vererek fromDbValueJan'ın cevabında benzer statik bir işlev oluşturarak bu sorunu çözmek .

public enum ReportTypeEnum {
    R1(1),
    R2(2),
    R3(3),
    R4(4),
    R5(5),
    R6(6),
    R7(7),
    R8(8);

    private static Logger log = LoggerFactory.getLogger(ReportEnumType.class);  
    private static Map<Integer, ReportTypeEnum> lookup;
    private Integer dbValue;

    private ReportTypeEnum(Integer dbValue) {
        this.dbValue = dbValue;
    }


    static {
        try {
            ReportTypeEnum[] vals = ReportTypeEnum.values();
            lookup = new HashMap<Integer, ReportTypeEnum>(vals.length);

            for (ReportTypeEnum  rpt: vals)
                lookup.put(rpt.getDbValue(), rpt);
         }
         catch (Exception e) {
             // Careful, if any exception is thrown out of a static block, the class
             // won't be initialized
             log.error("Unexpected exception initializing " + ReportTypeEnum.class, e);
         }
    }

    public static ReportTypeEnum fromDbValue(Integer dbValue) {
        return lookup.get(dbValue);
    }

    public Integer getDbValue() {
        return this.dbValue;
    }

}

Şimdi, sıralamayı değiştirmeden sıralamayı değiştirebilirsiniz.


Bu doğru cevap. (Daha sonraki kod değişiklikleri nedeniyle) diğer daha doğrudan ama potansiyel olarak geçersiz cevaplar ile karşılaştırıldığında çok az puan var şaşırdım.
Calabacin

8

Sen olabilir statik arama tablosu kullanın:

public enum Suit {
  spades, hearts, diamonds, clubs;

  private static final Map<Integer, Suit> lookup = new HashMap<Integer, Suit>();

  static{
    int ordinal = 0;
    for (Suit suit : EnumSet.allOf(Suit.class)) {
      lookup.put(ordinal, suit);
      ordinal+= 1;
    }
  }

  public Suit fromOrdinal(int ordinal) {
    return lookup.get(ordinal);
  }
}

3
Ayrıca bkz . Numaralamalar .
trashgod

11
Vaov! Vay canına! Bu elbette temiz, ama ... bilirsiniz - içimdeki C programcısı, tam bir HashMap tahsis ettiğinizi ve içinde aramalar yaptığınızı görünce acı içinde çığlık atıyor. elmaslar ve kulüpler! AC programcısı her biri için 1 bayt tahsis eder: 'const char CLUBS = 0;' vs ... Evet, bir HashMap araması O (1), ancak bir HashMap'in bellek ve CPU yükü, bu durumda doğrudan .values ​​() öğesini çağırmaktan çok daha büyük ve yavaş açlık kaynağı sağlar! İnsanların böyle yazması durumunda Java'nın böyle bir bellek domuzu olması hiç de şaşırtıcı değil ...
Leszek

2
Her program üçlü A oyununun performansını gerektirmez. Birçok durumda tip güvenliği, okunabilirlik, bakım kolaylığı, platformlar arası destek, çöp toplama, vb. İçin bellek ve CPU ticareti haklı çıkar. Bir sebepten dolayı daha yüksek seviyeli diller mevcuttur.
Ocak

3
Ancak anahtar aralığınız her zaman ise 0...(n-1), bir dizi daha az kod ve daha okunabilirdir; performans artışı sadece bir bonus. private static final Suit[] VALUES = values();ve public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }. Ekstra avantaj: sessizce null döndürmekten ziyade geçersiz sıralarda çöküyor. (Her zaman bir avantaj değil. Ama sık sık.)
Thomas

4

Ben bunu kullanıyorum. Yukarıdaki basit çözümlerden çok daha az "verimli" olduğunu iddia etmiyorum. Yaptığı şey, yukarıdaki çözümde geçersiz bir sıra değeri kullanıldığında "ArrayIndexOutOfBounds" ifadesinden çok daha net bir istisna mesajı sağlamaktır.

EnumSet javadoc'un yineleyicinin öğeleri doğal düzeninde döndürdüğü gerçeğini kullanır. Bu doğru değilse bir iddia var.

JUnit4 Testi nasıl kullanıldığını gösterir.

 /**
 * convert ordinal to Enum
 * @param clzz may not be null
 * @param ordinal
 * @return e with e.ordinal( ) == ordinal
 * @throws IllegalArgumentException if ordinal out of range
 */
public static <E extends Enum<E> > E lookupEnum(Class<E> clzz, int ordinal) {
    EnumSet<E> set = EnumSet.allOf(clzz);
    if (ordinal < set.size()) {
        Iterator<E> iter = set.iterator();
        for (int i = 0; i < ordinal; i++) {
            iter.next();
        }
        E rval = iter.next();
        assert(rval.ordinal() == ordinal);
        return rval;
    }
    throw new IllegalArgumentException("Invalid value " + ordinal + " for " + clzz.getName( ) + ", must be < " + set.size());
}

@Test
public void lookupTest( ) {
    java.util.concurrent.TimeUnit tu = lookupEnum(TimeUnit.class, 3);
    System.out.println(tu);
}

1

Proguard ile Android'de yaptığım şey bu:

public enum SomeStatus {
    UNINITIALIZED, STATUS_1, RESERVED_1, STATUS_2, RESERVED_2, STATUS_3;//do not change order

    private static SomeStatus[] values = null;
    public static SomeStatus fromInteger(int i) {
        if(SomeStatus.values == null) {
            SomeStatus.values = SomeStatus.values();
        }
        if (i < 0) return SomeStatus.values[0];
        if (i >= SomeStatus.values.length) return SomeStatus.values[0];
        return SomeStatus.values[i];
    }
}

kısa ve Proguard'da bir istisna hakkında endişelenmem gerekmiyor


1

Şunun gibi basit bir yöntem tanımlayabilirsiniz:

public enum Alphabet{
    A,B,C,D;

    public static Alphabet get(int index){
        return Alphabet.values()[index];
    }
}

Ve şöyle kullanın:

System.out.println(Alphabet.get(2));

0
public enum Suit implements java.io.Serializable, Comparable<Suit>{
  spades, hearts, diamonds, clubs;
  private static final Suit [] lookup  = Suit.values();
  public Suit fromOrdinal(int ordinal) {
    if(ordinal< 1 || ordinal> 3) return null;
    return lookup[value-1];
  }
}

test sınıfı

public class MainTest {
    public static void main(String[] args) {
        Suit d3 = Suit.diamonds;
        Suit d3Test = Suit.fromOrdinal(2);
        if(d3.equals(d3Test)){
            System.out.println("Susses");
        }else System.out.println("Fails");
    }
}

Daha verimli bir kodunuz varsa bizimle paylaştığınız için teşekkür ederim, Numaram büyük ve sürekli olarak binlerce kez denir.


Bence "ordinal <1 || ordinal> 4) null döndürürse;
geowar

0

Öyleyse bir yol ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];hangi işe yarar ve daha önce belirtildiği gibi kolay, ExampleEnum.values()her çağrı için yeni bir klonlanmış dizi döndürür. Bu gereksiz bir şekilde pahalı olabilir. Bunu diziyi önbelleğe alarak çözebiliriz ExampleEnum[] values = values(). Ayrıca önbelleğe alınmış dizimizin değiştirilmesine izin vermek "tehlikelidir". Birisi yazabilir ExampleEnum.values[0] = ExampleEnum.type2;Bu yüzden fazladan kopyalama yapmayan bir erişimci yöntemi ile özel yapardı.

private enum ExampleEnum{
    type0, type1, type2, type3;
    private static final ExampleEnum[] values = values();
    public static ExampleEnum value(int ord) {
        return values[ord];
    }
}

ExampleEnum.value(ordinal)İle ilişkili enum değerini almak için kullanırsınızordinal


-1

Her numaralandırmada, numaralandırma üyesi adıyla bir dize veren name () vardır.

Verilen enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name()verecek Heart.

Her numaralandırma, valueOf()ters işlemi gerçekleştirmek için bir numaralandırma türü ve bir dize alan bir yönteme sahiptir :

Enum.valueOf(Suit.class, "Heart")döner Suit.Heart.

Neden herkes ordinal kullanacak? Nanosaniye daha hızlı olabilir, ancak numaralandırma üyeleri değişirse güvenli değildir, başka bir geliştirici bazı kodların sıralı değerlere bağlı olduğunun farkında olmayabilir (özellikle soruda belirtilen JSP sayfasında, ağ ve veritabanı yükü tamamen dize üzerinde bir tamsayı kullanmayın).


2
Tamsayıları karşılaştırmak dizeleri karşılaştırmaktan daha hızlı mıdır?
HighCommander4

2
Ancak biri numaralandırmayı değiştirirse sıra sayısı değişecektir (Memvers ekler / yeniden sıralar). Bazen, özellikle ağ gecikmesinin bir tamsayı dizisini (bir dize) ve tek bir tamsayıyı karşılaştırmak arasındaki farkın 1.000.000 katı olduğu bir JSP sayfasında güvenlik değil hızla ilgilidir.
Tony BenBrahim

toString geçersiz kılınabilir, bu nedenle numaralandırma adını döndürmeyebilir. Yöntem adı () numaralandırma adını verir (son)
gerardw

kod yorumları ve uygulama sürümleri de bir şeydir (belki bir dosya biçimi daha basit ve bu şekilde daha küçüktür)
joe pelletier

Bu, oxbow_lakes'in cevabındakiyle aynı şeyi önerdiğinde neden indirildiğinden emin değilim. Ordinal kullanmaktan kesinlikle daha güvenlidir.
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.