ส่ง Int ไปยัง enum ใน Java


333

วิธีที่ถูกต้องในการส่ง Int ไปยัง enum ใน Java จะให้ enum ต่อไปนี้คืออะไร?

public enum MyEnum
{
    EnumValue1,
    EnumValue2
}


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

@RyuS นั่นไม่ซ้ำกับคำถามนี้
Mark Rotteveel

คำตอบ:


586

ลองดูMyEnum.values()[x]ว่าxจะต้องอยู่ที่ไหน0หรือ1คือเลขที่ถูกต้องสำหรับ enum นั้น

โปรดทราบว่าใน Java enums จริง ๆ แล้วเป็นคลาส (และค่า enum จึงเป็นวัตถุ) และทำให้คุณไม่สามารถส่งintหรือแม้กระทั่งIntegerกับ enum


116
+1: คุณอาจต้องการแคชMyEnum.values()เนื่องจากมีราคาแพง เช่นถ้าคุณเรียกมันว่าหลายร้อยครั้ง
Peter Lawrey

6
@ PeterLawrey เพียงเพื่อความสมบูรณ์คุณสามารถอธิบายว่าทำไมมันควรจะช้า? ฉันไม่เห็นเหตุผลชัดเจน
Tarrasch

48
@Tarrasch เป็นอาร์เรย์ที่ไม่แน่นอนค่า () ต้องส่งคืนสำเนาของอาร์เรย์ขององค์ประกอบในกรณีที่คุณเกิดการเปลี่ยนแปลง การสร้างสำเนานี้ในแต่ละครั้งค่อนข้างแพง
Peter Lawrey

9
@PeterLawrey ฉันเคยใช้ฮาเซลมากเมื่อเร็ว ๆ นี้ (ซึ่งทุกอย่างไม่เปลี่ยนรูป)! ขอบคุณสำหรับคำอธิบายที่ชัดเจนและกระชับ :)
Tarrasch

วิธีรับ 'x'
Beeing Jk

161

MyEnum.values()[x]เป็นการดำเนินการที่มีราคาแพง หากประสิทธิภาพเป็นกังวลคุณอาจต้องการทำสิ่งนี้:

public enum MyEnum {
    EnumValue1,
    EnumValue2;

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

44
ถ้าคุณต้องการหลีกเลี่ยงการบำรุงรักษาสวิตช์จากนั้นใช้คลาส: private final MyEnum [] myEnumValues ​​= MyEnum.values ​​(); การใช้งาน: myEnum = myEnumValues ​​[i];
Gili Nachum

23
@GiliNachum พูดอย่างแปลก ๆ แต่ปัญหาของวิธีนี้คือการบำรุงรักษา มันขัดกับหลักการ DRY ซึ่งหมายความว่าเมื่อใดก็ตามที่มีการเปลี่ยนแปลงค่า enum (เรียงลำดับใหม่เพิ่มมูลค่าลบมูลค่า) งบสวิตช์จะต้องได้รับการปรับปรุงพร้อมกัน ความคิดเห็นของ Gili บังคับให้ Java รักษาความสอดคล้องกับรายการค่า enum การเปลี่ยนแปลงค่า enum จะไม่ส่งผลต่อวิธีการทั้งหมด
Dandre Allison

3
@ LorenzoPolidori คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงคิดMyEnum.values()[x]ว่าการทำงานมีราคาแพง ฉันไม่รู้ว่ามันทำงานอย่างไรในรายละเอียด แต่สำหรับฉันแล้วดูเหมือนว่าการเข้าถึงองค์ประกอบในอาเรย์จะไม่ใช่เรื่องใหญ่อะไรเวลาคงที่ หากอาร์เรย์ต้องมีการสร้างจะต้องใช้เวลา O (n) ซึ่งเป็นเวลาเดียวกับการแก้ปัญหาของคุณ
brunsgaard

1
@brunsgaard ฉันถือว่าvalues()มีการสร้างอาร์เรย์ใหม่ทุกครั้งเนื่องจากอาร์เรย์มีการเปลี่ยนแปลงดังนั้นจึงไม่ปลอดภัยที่จะส่งคืนค่าเดียวกันหลาย ๆ ครั้ง คำสั่ง Switch ไม่จำเป็นต้องเป็น O (n) สามารถรวบรวมเพื่อข้ามตารางได้ ดังนั้นคำกล่าวอ้างของ Lorenzo จึงดูเป็นธรรม
MikeFHay

1
เวอร์ชันของ @Johnson Gili ไม่ต้องการการเปลี่ยนแปลงรหัสเพิ่มเติมนอกเหนือจากการเปลี่ยนแปลงในการประกาศ Enum หากคุณเพิ่มรายการใหม่ใน Enum myEnum = myEnumValues[i]จะยังคงส่งคืนiรายการที่ th ใน Enum โดยไม่มีการเปลี่ยนแปลง
Dandre Allison

45

หากคุณต้องการให้ค่าจำนวนเต็มของคุณคุณสามารถใช้โครงสร้างเช่นด้านล่าง

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 เพราะมันเน้นข้อเท็จจริงว่าค่าไม่จำเป็นต้องต่อเนื่องกัน
rhavin

นอกจากนี้ดูเหมือนว่าจะเป็นคำตอบเดียวที่ใช้งานได้หากคุณต้องการชุดจำนวนเต็ม (หรือซ้ำ ๆ ) แทนการใช้เลขลำดับเริ่มต้นจาก 0 .. (count-1) สิ่งนี้อาจสำคัญหากคุณโต้ตอบกับรหัสที่มีอยู่เช่นผ่านเครือข่าย
benkc

น่าจะชี้ให้เห็นว่าในคำตอบข้างต้นการแคชผลลัพธ์ของค่า () อาจคุ้มค่าเพื่อหลีกเลี่ยงการจัดสรรหน่วยความจำและจัดเรียงสำเนาทุกครั้งที่คุณเรียกใช้
benkc

1
และขึ้นอยู่กับความยาวของ enum ของคุณคุณอาจต้องการสร้าง HashMap หรือใช้การค้นหาแบบไบนารีหรือบางอย่างสำหรับการค้นหานี้แทนที่จะทำการค้นหาเชิงเส้นทุกครั้ง
benkc

2
นี่ควรเป็นวิธีที่ถูกต้องและวิธีปฏิบัติที่ดีที่สุดในการแปลง int เป็น enum และฉันคิดว่าคุณสามารถทำให้ปัญหาง่ายขึ้นโดย A GetValue (int _id สาธารณะ) {สำหรับ (A: A.values ​​() (ถ้า a.getId () ) == _ id) {return a;}} return null;} กำจัดสิ่งที่ไม่มี, isEmpty () และเปรียบเทียบ ()
Chris.Zou

35

คุณสามารถลองสิ่งนี้
สร้างคลาสด้วยรหัสองค์ประกอบ

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

ตอนนี้ดึง Enum นี้โดยใช้ id เป็น int

MyEnum myEnum = MyEnum.fromId(5);

19

ฉันแคชค่าและสร้างวิธีการเข้าถึงแบบคงที่แบบง่าย:

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
นี่เป็นวิธีแก้ปัญหาที่ฉันใช้ตอนนี้ แต่ IMHO มันจะทำให้เกิดความสับสนน้อยถ้าคุณไม่ให้สนามชื่อเดียวกับวิธีการvalues values()ฉันใช้cachedValuesสำหรับชื่อฟิลด์
ToolmakerSteve

2
มีประสิทธิภาพและสง่างาม คัดลอกและวางในโครงการของฉัน :) สิ่งเดียวที่ฉันเปลี่ยนคือ fromInt (int i) ซึ่งฉันเพิ่งถูกเรียกจาก (int i) เพราะมันซ้ำซ้อนเล็กน้อยที่จะมี int สองครั้งในลายเซ็น
pipedreambomb

1
ทำไมไม่เริ่มต้นจากจุดเริ่มต้น? เหตุใดจึงต้องรอการโจมตีแรก
ไกเซอร์

9

Java enums ไม่มีการแม็พ enum-to-int แบบเดียวกับที่ทำใน C ++

ที่กล่าวว่า enums ทั้งหมดมีvaluesวิธีที่ส่งกลับอาร์เรย์ของค่า enum ที่เป็นไปได้ดังนั้น

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

ควรทำงาน. มันน่ารังเกียจนิดหน่อยและมันอาจเป็นการดีกว่าถ้าไม่ลองและแปลงจากints เป็นEnums (หรือกลับกัน) ถ้าเป็นไปได้


9

นี่ไม่ใช่สิ่งที่มักจะทำดังนั้นฉันจะพิจารณาอีกครั้ง แต่ต้องบอกว่าการดำเนินการพื้นฐานคือ: int -> enum โดยใช้ EnumType.values ​​() [intNum] และ enum -> int โดยใช้ enumInst.ordinal ()

อย่างไรก็ตามเนื่องจากการใช้งานค่าใด ๆ () ไม่มีทางเลือกนอกจากให้สำเนาของอาร์เรย์ (java array ไม่เคยอ่านอย่างเดียว) คุณจะได้รับการบริการที่ดีขึ้นโดยใช้ EnumMap เพื่อแคชการแม็ป enum -> int


2
Re "นี่ไม่ใช่สิ่งที่มักจะทำ": กรณีทั่วไปที่มีประโยชน์: enum สอดคล้องกับค่า int ที่เก็บไว้ในฐานข้อมูล
ToolmakerSteve

@ToolmakerSteve คุณมีสิทธิ์อย่างยิ่งที่จะต้องทำการแมป แต่คุณต้องการที่จะปล่อยให้การเข้ารหัสแบบนั้นกับผู้ทำแผนที่ OR หรือเครื่องมือ / ไลบรารีบางส่วน?
Dilum Ranatunga


6

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

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

ฉันต้องแปลง id เป็น enums ตามเวลาที่กำหนดเท่านั้น (เมื่อโหลดข้อมูลจากไฟล์) ดังนั้นจึงไม่มีเหตุผลที่ฉันจะเก็บแผนที่ไว้ในหน่วยความจำตลอดเวลา หากคุณต้องการให้แผนที่สามารถเข้าถึงได้ตลอดเวลาคุณสามารถแคชในฐานะสมาชิกแบบคงที่ของคลาส Enum ของคุณ


IMHO ถ้าฉันกังวลเกี่ยวกับการใช้หน่วยความจำฉันจะสร้าง HashMap - เหมือน @ossys แบบไดนามิก แต่ด้วยรหัสที่แตกต่างกันเมื่อแคชเป็นโมฆะจากนั้นเพิ่มวิธีที่สองclearCachedValuesเมื่อคุณใช้งานเสร็จแล้ว (ตั้งค่าฟิลด์ส่วนตัวกลับเป็นโมฆะ) ฉันคิดว่าMyEnum.fromInt(i)เข้าใจง่ายกว่าผ่านวัตถุแผนที่
ToolmakerSteve

6

ในกรณีที่ช่วยเหลือผู้อื่นตัวเลือกที่ฉันชอบซึ่งไม่ได้อยู่ในรายการนี้จะใช้ฟังก์ชั่นแผนที่ของ Guava :

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

ด้วยค่าเริ่มต้นที่คุณสามารถใช้nullคุณสามารถthrow IllegalArgumentExceptionหรือคุณfromIntสามารถคืนค่าOptionalพฤติกรรมที่คุณต้องการ


3
คุณควรพูดถึงว่าคุณกำลังใช้ Guava หรือคุณสามารถใช้สตรีม:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel

1
อาจต้องการกำหนดgetValue()วิธีการด้วย
shmosel

@shmosel โอ๊ะฉันพลาดฟังก์ชั่น getValue ขอบคุณ การพิมพ์เวอร์ชันทั่วไปลงใน stackoverflow ไม่ได้เลื่อนออกไปเสมอ เพิ่มความคิดเห็นเกี่ยวกับการใช้ Guava ด้วยลิงก์ ชอบวิธี Guava เพื่อส่งกระแสข้อมูล
แช้ด Befus

4

คุณสามารถวนซ้ำvalues()enum และเปรียบเทียบค่าจำนวนเต็มของ enum ได้idดังนี้:

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

และใช้แบบนี้:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
ฉันหวังว่านี่จะช่วยได้;)


3

จากคำตอบของ @ChadBefus และความคิดเห็น @shmosel ฉันขอแนะนำให้ใช้สิ่งนี้ (การค้นหาที่มีประสิทธิภาพและทำงานบน pure java> = 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

ตัวเลือกที่ดีคือหลีกเลี่ยงการแปลงจากintเป็นenum: ตัวอย่างเช่นหากคุณต้องการค่าสูงสุดคุณอาจเปรียบเทียบ x.ordinal () กับ y.ordinal () และส่งคืน x หรือ y ตามลำดับ (คุณอาจจำเป็นต้องสั่งซื้อใหม่อีกครั้งเพื่อให้การเปรียบเทียบมีความหมาย)

หากเป็นไปไม่ได้ฉันจะเก็บไว้MyEnum.values()ในอาร์เรย์คงที่


1
หากคุณได้รับ int จาก DB และต้องการแปลงเป็น Enum ซึ่งฉันคิดว่าเป็นงานที่ธรรมดามากคุณต้องมี int -> enum conversion IMO ..
Yuki Inoue

0

นี่เป็นคำตอบเดียวกับแพทย์ แต่มันแสดงวิธีกำจัดปัญหาด้วยอาร์เรย์ที่ไม่แน่นอน หากคุณใช้วิธีการนี้เนื่องจากการคาดคะเนสาขาก่อนหากจะมีผลกระทบน้อยมากถึงศูนย์และรหัสทั้งหมดจะเรียกเฉพาะค่าอาร์เรย์ที่ไม่แน่นอน () ฟังก์ชั่นเพียงครั้งเดียว เนื่องจากตัวแปรทั้งสองเป็นแบบสแตติกพวกเขาจะไม่ใช้หน่วยความจำ n * สำหรับการใช้การแจงนับนี้ทุกครั้ง

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 ฉันเชื่อว่าข้อยกเว้นจะดีกว่าอย่างมีนัยสำคัญหากขอบเขตไม่มากนัก
โซโซ

ความเชื่อของคุณขึ้นอยู่กับอะไร?
shmosel

0

ใน Kotlin:

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

การใช้งาน:

val status = Status.getStatus(1)!!

0

เขียนการใช้งานนี้ จะช่วยให้ค่าที่หายไปค่าลบและเก็บรหัสสอดคล้อง แผนที่ถูกแคชเช่นกัน ใช้อินเตอร์เฟสและต้องการ Java 8

enum

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

อินเตอร์เฟซ

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;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.