วิธีรับ Enum Value จากดัชนีใน Java


97

ฉันมี enum ใน Java:

public enum Months
{
    JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC
}

ฉันต้องการเข้าถึงค่า enum ด้วยดัชนีเช่น

Months(1) = JAN;
Months(2) = FEB;
...

ฉันจะทำอย่างไร


12
ในวิทยาการคอมพิวเตอร์ดัชนีเริ่มต้นที่ 0 ไม่ใช่ 1 ;-)
fredoverflow

1
คุณแน่ใจหรือว่าคุณต้องการ? โดยทั่วไปคุณไม่ควรสัมผัสลำดับนอกจากการใช้โครงสร้างข้อมูลระดับต่ำ (จากนั้นใช้กลไกอื่นเช่นชื่อเพื่อการคงอยู่)
Tom Hawtin - แท็

คุณสามารถใช้ค่าคงที่ในคลาส java.util.Calendar ได้เช่นกัน โดยจะมีเลข 0 - 11 สำหรับ ม.ค. - ธ.ค. ระวัง 12 เพราะนั่นคือ UnDe December (เกี่ยวกับปฏิทินจันทรคติ) แต่ฉันแค่อยากรู้ว่าทำไมต้องประดิษฐ์วงล้อของค่าคงที่ของเดือนที่มาพร้อมกับ "หุ้น" ใน JRE?
Chris Aldrich

2FredOverflow: Aggree ฉันใช้ดัชนีผิด 2Tom Hawtin: ใช่ฉันแน่ใจ ฉันยังคงใช้ข้อมูลกับกรอบงานบางส่วนและฉันได้รับดัชนีจำนวนเต็มกลับไม่ใช่ enum 2Chris Aldrich: นี่เป็นเพียงตัวอย่างจำลองที่ไม่ตรงกับกรณีจริง
jk_

อย่างไรก็ตาม Java 8 และใหม่กว่ามาพร้อมกับMonthenum ในตัว
Basil Bourque

คำตอบ:


234

ลองทำตามนี้

Months.values()[index]

39
โปรดทราบว่าจะคัดลอกสำเนาของอาร์เรย์ค่าทุกครั้งดังนั้นหากคุณเรียกสิ่งนี้ในวงในของรหัสที่ละเอียดอ่อนด้านประสิทธิภาพคุณอาจต้องการสร้างสำเนาแบบคงที่และใช้สิ่งนั้น
Christopher Barber

1
ฉันสับสนแล้วทำไมฉันไม่ต้องการใช้อาร์เรย์แทน
Anudeep Samaiya

@AnudeepSamaiya อาจเป็นเพราะเราต้องการใช้ค่าคงที่ enum ที่เหมาะสม (Months.JAN) ในรหัสแทนเดือน [1] ทุกที่
Harry Joy

@Christopher Barber นี่คือซับเดียวสำหรับ "การทำสำเนาแบบคงที่": public static final ArrayList<Months> ALL = new ArrayList<Month>() {{ for (Months m : Months.values()) add(m); }};จากนั้นคุณสามารถเข้าถึงองค์ประกอบได้ด้วยMonths i = ALL.get(index)
muelleth

มันจะง่ายกว่าถ้าคุณใช้ Months.values ​​(). clone () หรือถ้าคุณหวาดระแวงว่าจะห่อไว้ในรายการที่ไม่เปลี่ยนรูปได้ (ดูคอลเล็กชัน)
Christopher Barber

20

มีสามวิธีในการทำ

public enum Months {
    JAN(1), FEB(2), MAR(3), APR(4), MAY(5), JUN(6), JUL(7), AUG(8), SEP(9), OCT(10), NOV(11), DEC(12);


    int monthOrdinal = 0;

    Months(int ord) {
        this.monthOrdinal = ord;
    }

    public static Months byOrdinal2ndWay(int ord) {
        return Months.values()[ord-1]; // less safe
    }

    public static Months byOrdinal(int ord) {
        for (Months m : Months.values()) {
            if (m.monthOrdinal == ord) {
                return m;
            }
        }
        return null;
    }
    public static Months[] MONTHS_INDEXED = new Months[] { null, JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };

}




import static junit.framework.Assert.assertEquals;

import org.junit.Test;

public class MonthsTest {

@Test
public void test_indexed_access() {
    assertEquals(Months.MONTHS_INDEXED[1], Months.JAN);
    assertEquals(Months.MONTHS_INDEXED[2], Months.FEB);

    assertEquals(Months.byOrdinal(1), Months.JAN);
    assertEquals(Months.byOrdinal(2), Months.FEB);


    assertEquals(Months.byOrdinal2ndWay(1), Months.JAN);
    assertEquals(Months.byOrdinal2ndWay(2), Months.FEB);
}

}

5
public staticไม่แน่นอน (ทั้งอาร์เรย์และไม่final) อู้ว. และIllegalArgumentExceptionจะมีเหตุผลมากกว่าการคืนnullระเบิด
Tom Hawtin - แท็

4

ฉันเพิ่งลองแบบเดียวกันและหาวิธีแก้ปัญหาต่อไปนี้:

public enum Countries {
    TEXAS,
    FLORIDA,
    OKLAHOMA,
    KENTUCKY;

    private static Countries[] list = Countries.values();

    public static Countries getCountry(int i) {
        return list[i];
    }

    public static int listGetLastIndex() {
        return list.length - 1;
    }
}

คลาสมีค่าของตัวเองที่บันทึกไว้ในอาร์เรย์และฉันใช้อาร์เรย์เพื่อรับ enum ที่ indexposition ดังที่กล่าวไว้ข้างต้นอาร์เรย์จะเริ่มนับจาก 0 หากคุณต้องการให้ดัชนีของคุณเริ่มจาก '1' เพียงแค่เปลี่ยนสองวิธีนี้เป็น:

public static String getCountry(int i) {
    return list[(i - 1)];
}

public static int listGetLastIndex() {
    return list.length;
}

ภายใน Main ของฉันฉันได้รับประเทศ - วัตถุที่จำเป็นด้วย

public static void main(String[] args) {
   int i = Countries.listGetLastIndex();
   Countries currCountry = Countries.getCountry(i);
}

ซึ่งตั้งค่า currCountry เป็นประเทศสุดท้ายในกรณีนี้คือ Countries.KENTUCKY

เพียงจำไว้ว่ารหัสนี้ได้รับผลกระทบอย่างมากจาก ArrayOutOfBoundsExceptions หากคุณใช้ตัวบ่งชี้ที่เข้ารหัสเพื่อรับวัตถุของคุณ


2

เมื่อเร็ว ๆ นี้ฉันมีปัญหาเดียวกันและใช้วิธีแก้ปัญหาที่ Harry Joy ให้มา โซลูชันนั้นใช้ได้เฉพาะกับการแจงนับแบบศูนย์เท่านั้น ฉันจะไม่คิดว่ามันประหยัดเพราะมันไม่ได้จัดการกับดัชนีที่อยู่นอกช่วง

วิธีแก้ปัญหาที่ฉันใช้อาจไม่ง่ายอย่างที่คิด แต่มันช่วยประหยัดได้อย่างสมบูรณ์และจะไม่ทำร้ายประสิทธิภาพของโค้ดของคุณแม้จะมี enums ขนาดใหญ่:

public enum Example {

    UNKNOWN(0, "unknown"), ENUM1(1, "enum1"), ENUM2(2, "enum2"), ENUM3(3, "enum3");

    private static HashMap<Integer, Example> enumById = new HashMap<>();
    static {
        Arrays.stream(values()).forEach(e -> enumById.put(e.getId(), e));
    }

    public static Example getById(int id) {
        return enumById.getOrDefault(id, UNKNOWN);
    }

    private int id;
    private String description;

    private Example(int id, String description) {
        this.id = id;
        this.description= description;
    }

    public String getDescription() {
        return description;
    }

    public int getId() {
        return id;
    }
}

หากคุณแน่ใจว่าดัชนีของคุณจะไม่อยู่นอกขอบเขตและคุณไม่ต้องการใช้UNKNOWNเหมือนที่ฉันทำข้างต้นแน่นอนคุณสามารถทำได้เช่นกัน:

public static Example getById(int id) {
        return enumById.get(id);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.