แปลงจาก enum ordinal เป็น enum type


316

ฉันเป็นประเภท enum ReportTypeEnumที่ได้รับการส่งระหว่างเมธอดในคลาสทั้งหมดของฉัน แต่ฉันต้องผ่านค่านี้ใน URL ดังนั้นฉันจึงใช้เมธอดลำดับเพื่อรับค่า int หลังจากที่ฉันได้รับมันในหน้า JSP อื่นของฉันฉันต้องแปลงมันกลับไปที่หน้าReportTypeEnumเพื่อให้ฉันสามารถผ่านมันต่อไปได้

ฉันจะแปลงลำดับไปReportTypeEnum?

ใช้ Java 6 SE


1
ไม่มี Java 6 EE จนกระทั่งตอนนี้ (AFAIK) มี Java SE 6 และ Java EE 5
Hosam Aly

คำตอบ:


632

หากต้องการแปลงเลขลำดับเป็นตัวเลือกการตอบกลับของ Enum คุณอาจต้องการทำสิ่งนี้:

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

โปรดสังเกตขอบเขตของอาร์เรย์

โปรดทราบว่าทุกการเรียกเพื่อvalues()ส่งคืนอาร์เรย์ที่โคลนใหม่ซึ่งอาจส่งผลกระทบต่อประสิทธิภาพในทางลบ คุณอาจต้องการแคชอาร์เรย์ถ้ามันจะถูกเรียกบ่อย

values()ตัวอย่างโค้ดเกี่ยวกับวิธีการแคช


คำตอบนี้ถูกแก้ไขเพื่อรวมข้อเสนอแนะที่ได้รับภายในความคิดเห็น


ฉันใช้โซลูชันนี้และไม่ได้ผลสำหรับฉัน จะส่งคืนค่าลำดับไม่รับประกันว่าจะตรงกับคำสั่งที่เพิ่มประเภทที่ระบุ ฉันไม่ทราบว่าเป็นสิ่งที่คำตอบนี้สนับสนุน แต่ฉันต้องการเตือนผู้คนถึงกระนั้น
IcedDante

@IcesDante: รับประกันอันดับที่แน่นอนเพื่อให้สอดคล้องกับคำสั่งของค่าการแจงนับในแหล่งที่มา หากคุณสังเกตเห็นพฤติกรรมที่แตกต่างจากนั้นมีสิ่งอื่นที่ผิด คำตอบของฉันข้างต้นนั้นไม่ดีพอสำหรับเหตุผลทั้งหมดที่วางไว้ในคำตอบอื่น ๆ
Joachim Sauer

@JoachimSauer บางที IcedDante หมายถึงเลขลำดับอาจไม่ตรงกันหากมีการผลิตและบันทึกโดยแหล่งที่มารุ่นก่อนหน้าซึ่งมีค่า enum อยู่ในลำดับที่แตกต่างกัน
LarsH

137

นี้เป็นเกือบแน่นอนความคิดที่ดี แน่นอนถ้าลำดับที่ถูกยกเลิกโดยพฤตินัย (เช่นเนื่องจากมีคนทำบุ๊กมาร์ก URL) - หมายความว่าคุณจะต้องรักษาenumลำดับไว้ในอนาคตเสมอซึ่งอาจไม่ชัดเจนว่าผู้ดูแลรหัสลงบรรทัด

ทำไมไม่เข้ารหัสการenumใช้myEnumValue.name()(และถอดรหัสผ่านReportTypeEnum.valueOf(s)) แทน?


24
เกิดอะไรขึ้นถ้าคุณเปลี่ยนชื่อของ enum (แต่เก็บคำสั่งไว้)
Arne Evertsson

6
@Arne - ฉันคิดว่านี่เป็นโอกาสน้อยกว่าคนที่ไม่มีประสบการณ์เข้ามาและเพิ่ม a valueที่จุดเริ่มต้นหรือตำแหน่งตามตัวอักษร / ตรรกะที่ถูกต้อง (โดยตรรกะผมหมายถึงตัวอย่างเช่นTimeUnitค่ามีตำแหน่งตรรกะ)
oxbow_lakes

7
แน่นอนฉันต้องการบังคับให้คำสั่ง enums มากกว่าชื่อ enum ของฉัน ... นี่คือเหตุผลที่ฉันต้องการเก็บลำดับที่มากกว่าชื่อ enum ในฐานข้อมูล นอกจากนี้ยังเป็นการดีกว่าที่จะใช้การจัดการอย่าง

8
ฉันเห็นด้วย. ใน API สาธารณะการเปลี่ยนชื่อของ Enum จะทำให้ความเข้ากันได้ย้อนหลัง แต่การเปลี่ยนลำดับจะไม่ ด้วยเหตุนี้จึงเหมาะสมกว่าที่จะใช้ชื่อเป็น "กุญแจ" ของคุณ
Noel

3
การจัดลำดับที่ทำให้การแปลความคิดของคุณเป็นภาษาอื่นง่ายขึ้น ถ้าคุณต้องการเขียนองค์ประกอบใน C?
QED

94

ถ้าฉันจะใช้values()มาก:

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

ในขณะเดียวกันที่ไหนก็ตาม. java:

Suit suit = Suit.values[ordinal];

นึกถึงอาเรย์ของคุณ


3
+1 นี่คือทางออกที่ดีที่สุด IMHO เพราะใครสามารถผ่านลำดับโดยเฉพาะอย่างยิ่งใน android.os.Message
likejudo

9
นี้เป็นกังวลมากเพราะอาร์เรย์มีความไม่แน่นอน แม้ว่าค่า [] ถือเป็นที่สิ้นสุด แต่ก็ไม่ได้ป้องกันSuit.values[0] = Suit.Diamonds;บางส่วนในรหัสของคุณ เป็นการดีที่จะไม่เกิดขึ้น แต่หลักการโดยรวมของการไม่เปิดเผยฟิลด์ที่ไม่แน่นอนที่ยังคงอยู่ สำหรับวิธีนี้ลองใช้Collections.unmodifiableListหรือสิ่งที่คล้ายกันแทน
Mshnik

วิธีการเกี่ยวกับ - ค่าคงที่ส่วนตัวสุดท้ายคงที่ [] = ค่า (); ชุดสาธารณะคงที่ [] getValues ​​() {ค่าส่งคืน; }
Pratyush

4
@Pratyush ที่จะทำให้ตัวแปรอาร์เรย์ไม่เปลี่ยนรูป แต่ไม่ใช่เนื้อหา ฉันยังสามารถทำ getValues ​​() [0] = somethingElse;
Calabacin

เห็นด้วยจึงจุดที่ไม่ได้เปิดเผยSuit values[]โดยตรงหรือโดยอ้อม (วิธีการที่ถูกกล่าวถึงผ่านgetValues()) ผมทำงานรอบกับวิธีการของประชาชนที่ordinalคุ้มค่าจะต้องถูกส่งวิธีการโต้แย้งและกลับมาเป็นตัวแทนจากSuit Suit values[]จุดที่นี่ (
Manuel Jordan

13

ฉันเห็นด้วยกับคนส่วนใหญ่ว่าการใช้ลำดับอาจเป็นความคิดที่ไม่ดี ฉันมักจะแก้ปัญหานี้โดยให้ตัวสร้าง enum ส่วนตัวที่สามารถใช้ตัวอย่างเช่นค่า DB แล้วสร้างfromDbValueฟังก์ชั่นคงที่คล้ายกับหนึ่งในคำตอบของ ม.ค.

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

}

ตอนนี้คุณสามารถเปลี่ยนลำดับโดยไม่ต้องเปลี่ยนการค้นหาและในทางกลับกัน


นี่คือคำตอบที่ถูกต้อง ฉันประหลาดใจที่มีคะแนนน้อยมากเมื่อเทียบกับคำตอบอื่น ๆ ที่ตรงกว่า แต่อาจไม่ถูกต้อง (เนื่องจากการเปลี่ยนแปลงรหัสในอนาคต)
Calabacin

8

คุณสามารถใช้ตารางการค้นหาแบบคงที่:

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
ดูEnumsด้วย
trashgod

11
ว้าว! แค่ว้าว! แน่นอนว่าเป็นเรื่องเรียบร้อย แต่ ... คุณรู้ - โปรแกรมเมอร์ C ในตัวฉันกรีดร้องด้วยความเจ็บปวดเมื่อเห็นว่าคุณจัดสรร HashMap ที่เป่าเต็มแล้วและทำการค้นหาภายในมันทั้งหมดเพียงแค่จัดการค่าคงที่ 4 หลัก: โพดำ, หัวใจ, เพชรและคลับ! โปรแกรมเมอร์ AC จะจัดสรร 1 ไบต์สำหรับแต่ละรายการ: 'const char CLUBS = 0;' ฯลฯ ... ใช่การค้นหา HashMap คือ O (1) แต่หน่วยความจำและซีพียูโอเวอร์เฮดของ HashMap ในกรณีนี้ทำให้คำสั่งจำนวนมากช้าลงและทรัพยากรหิวกว่าการโทรหาค่า () โดยตรง! ไม่น่าแปลกใจ Java เป็นเช่นหมูหน่วยความจำถ้าคนเขียนเช่นนี้ ...
Leszek

2
ไม่ใช่ทุกโปรแกรมที่ต้องการประสิทธิภาพของเกมสามเท่า ในหลายกรณีการซื้อขายหน่วยความจำและ CPU เพื่อความปลอดภัยประเภทการอ่านการบำรุงรักษาการสนับสนุนข้ามแพลตฟอร์มการรวบรวมขยะ ฯลฯ ... เป็นสิ่งที่สมเหตุสมผล มีภาษาระดับสูงกว่าด้วยเหตุผล
Jan

3
แต่ถ้าช่วงคีย์ของคุณอยู่เสมอ0...(n-1)อาร์เรย์ก็จะมีโค้ดน้อยลงและอ่านง่ายขึ้นเช่นกัน การเพิ่มประสิทธิภาพเป็นเพียงโบนัส และprivate static final Suit[] VALUES = values(); public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }ข้อได้เปรียบพิเศษ: ล้มเหลวในลำดับที่ไม่ถูกต้องในทันทีแทนที่จะส่งคืนค่าว่าง (ไม่ใช่ข้อได้เปรียบเสมอไป แต่บ่อยครั้ง)
โธมัส

4

นี่คือสิ่งที่ฉันใช้ ฉันไม่คิดว่ามันจะ "มีประสิทธิภาพ" น้อยไปกว่าโซลูชันที่ง่ายกว่าด้านบน สิ่งที่มันทำคือให้ข้อความข้อยกเว้นที่ชัดเจนกว่า "ArrayIndexOutOfBounds" เมื่อใช้ค่าเลขลำดับที่ไม่ถูกต้องในการแก้ปัญหาข้างต้น

มันใช้ข้อเท็จจริงที่ว่า EnumSet javadoc ระบุตัววนซ้ำส่งคืนองค์ประกอบตามลำดับโดยธรรมชาติ มีการยืนยันหากไม่ถูกต้อง

การทดสอบ JUnit4 แสดงให้เห็นถึงวิธีการใช้งาน

 /**
 * 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

นี่คือสิ่งที่ฉันทำบน Android กับ Proguard:

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

มันสั้นและฉันไม่ต้องกังวลกับการมีข้อยกเว้นใน Proguard


1

คุณสามารถกำหนดวิธีการง่ายๆเช่น:

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

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

และใช้มันเช่น:

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

ชั้นทดสอบ

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

ฉันขอขอบคุณที่คุณแบ่งปันกับเราหากคุณมีรหัสที่มีประสิทธิภาพยิ่งขึ้น Enum ของฉันมีขนาดใหญ่และเรียกได้หลายพันครั้งอย่างต่อเนื่อง


ฉันคิดว่าคุณหมายถึง "if (ordinal <1 || ordinal> 4) return null;"
geowar

0

ดังนั้นวิธีหนึ่งคือการExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];ทำงานที่ได้ผลและง่ายอย่างไรก็ตามตามที่กล่าวไว้ก่อนหน้าจะExampleEnum.values()ส่งคืนอาร์เรย์โคลนใหม่สำหรับการโทรทุกครั้ง ที่อาจมีราคาแพงโดยไม่จำเป็น ExampleEnum[] values = values()เราสามารถแก้ปัญหาว่าด้วยการแคชอาร์เรย์เช่นดังนั้น นอกจากนี้ยังเป็น "อันตราย" เพื่ออนุญาตให้เราปรับเปลี่ยนอาร์เรย์แคช มีคนเขียนได้ExampleEnum.values[0] = ExampleEnum.type2;ดังนั้นฉันจะทำให้เป็นส่วนตัวด้วยวิธีการเข้าถึงที่ไม่ได้คัดลอกพิเศษ

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)เพื่อรับค่า enum ที่เกี่ยวข้องกับordinal


-1

ทุก enum มีชื่อ () ซึ่งให้สตริงที่มีชื่อของสมาชิก enum

ป.ร. ให้enum Suit{Heart, Spade, Club, Diamond}ไว้Suit.Heart.name()จะให้Heartจะให้

ทุก Enum มี valueOf()เมธอดซึ่งใช้ชนิด enum และสตริงเพื่อดำเนินการย้อนกลับ:

Enum.valueOf(Suit.class, "Heart")Suit.Heartผลตอบแทน

ทำไมทุกคนจะใช้กฎอยู่นอกเหนือฉัน อาจเร็วกว่า nanoseconds แต่ไม่ปลอดภัยหากสมาชิก enum เปลี่ยนเนื่องจากนักพัฒนารายอื่นอาจไม่ทราบว่าโค้ดบางตัวอาศัยค่าลำดับ (โดยเฉพาะอย่างยิ่งในหน้า JSP ที่อ้างถึงในคำถามเครือข่ายและฐานข้อมูลเหนือศีรษะทั้งหมด เวลาไม่ใช้จำนวนเต็มในสตริง)


2
เพราะการเปรียบเทียบจำนวนเต็มเร็วกว่าการเปรียบเทียบสตริง?
HighCommander4

2
แต่เลขลำดับจะเปลี่ยนหากมีคนแก้ไข enum (เพิ่ม / จัดลำดับตัวบันทึกใหม่) บางครั้งมันเกี่ยวกับความปลอดภัยและไม่เร็วโดยเฉพาะในหน้า JSP ที่เวลาแฝงเครือข่ายอยู่ที่ 1,000,000 เท่าความแตกต่างระหว่างการเปรียบเทียบอาร์เรย์ของจำนวนเต็ม (สตริง) และจำนวนเต็มเดียว
Tony BenBrahim

toString สามารถแทนที่ได้ดังนั้นจึงอาจไม่ส่งคืนชื่อ enum ชื่อเมธอด () คือสิ่งที่ให้ชื่อ enum (เป็นที่สิ้นสุด)
gerardw

ความคิดเห็นเกี่ยวกับโค้ดและเวอร์ชันของแอปพลิเคชันก็เป็นเรื่องหนึ่งเช่นกัน (รูปแบบไฟล์อาจจะง่ายกว่าและเล็กกว่านี้)
โจ Pelletier

ไม่แน่ใจว่าทำไมสิ่งนี้จึงถูกลดระดับลงเมื่อมีการแนะนำสิ่งเดียวกันเช่นเดียวกับในคำตอบของ oxbow_lakes ปลอดภัยกว่าการใช้ลำดับ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.