Truyền Int cho enum trong Java


Câu trả lời:


583

Hãy thử MyEnum.values()[x]nơi xphải 0hoặc 1, tức là một thứ tự hợp lệ cho enum đó.

Lưu ý rằng trong enum Java thực sự là các lớp (và các giá trị enum là các đối tượng) và do đó bạn không thể truyền một inthoặc thậm chí Integercho một enum.


115
+1: Bạn có thể muốn lưu trữ MyEnum.values()vì nó đắt tiền. tức là nếu bạn gọi nó hàng trăm lần.
Peter Lawrey

6
@PeterLawrey Chỉ để hoàn thiện, bạn có thể giải thích tại sao nó phải chậm không? Tôi thấy không có lý do rõ ràng cho nó.
Tarrasch

48
@Tarrasch là mảng có thể thay đổi, các giá trị () phải trả về một bản sao của mảng các phần tử chỉ trong trường hợp bạn tình cờ thay đổi nó. Tạo bản sao này mỗi lần là tương đối tốn kém.
Peter Lawrey

9
@PeterLawrey Gần đây tôi đã sử dụng rất nhiều haskell (nơi mọi thứ đều bất biến)! Cảm ơn lời giải thích rõ ràng và súc tích của bạn. :)
Tarrasch

làm thế nào để có được 'x'?
Nuôi ong Jk

160

MyEnum.values()[x]là một hoạt động đắt tiền. Nếu hiệu suất là một mối quan tâm, bạn có thể muốn làm một cái gì đó như thế này:

public enum MyEnum {
    EnumValue1,
    EnumValue2;

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

44
Nếu bạn muốn tránh bảo trì chuyển đổi, thì trên lớp sử dụng: private Final MyEnum [] myEnumValues ​​= MyEnum.values ​​(); Sau đó sử dụng: myEnum = myEnumValues ​​[i];
Gili Nachum

23
@GiliNachum đã nói điều đó một cách kỳ lạ, nhưng vấn đề với giải pháp này là khả năng bảo trì. Nó đi ngược lại nguyên tắc DRY, có nghĩa là bất cứ khi nào các giá trị enum được thay đổi (sắp xếp lại, giá trị được thêm vào, giá trị được loại bỏ), câu lệnh chuyển đổi phải được cập nhật đồng thời. Nhận xét của Gili buộc Java phải duy trì tính nhất quán với danh sách các giá trị enum, các thay đổi đối với các giá trị enum hoàn toàn không ảnh hưởng đến cách tiếp cận đó.
Dandre Allison

3
@LorenzoPolidori, Bạn có thể giải thích lý do tại sao bạn coi MyEnum.values()[x]là một hoạt động đắt tiền. Tôi không biết làm thế nào nó hoạt động chi tiết, nhưng với tôi có vẻ như việc truy cập một phần tử trong Mảng sẽ không phải là vấn đề lớn, hay còn gọi là thời gian không đổi. Nếu mảng phải được xây dựng thì phải mất thời gian O (n), đó là thời gian chạy giống như giải pháp của bạn.
brungaard

1
@brunsgaard Tôi giả sử values()tạo ra một mảng mới mỗi lần, bởi vì các mảng có thể thay đổi được nên sẽ không an toàn khi trả về cùng một lần nhiều lần. Các câu lệnh chuyển đổi không nhất thiết phải là O (n), chúng có thể được biên dịch thành các bảng nhảy. Vì vậy, tuyên bố của Lorenzo có vẻ hợp lý.
MikeFHay

1
Phiên bản của @Johnson Gili không yêu cầu bất kỳ thay đổi mã bổ sung nào ngoài các thay đổi trong khai báo Enum. Nếu bạn thêm một mục mới vào enum, myEnum = myEnumValues[i]vẫn sẽ trả lại imục thứ trong Enum mà không thay đổi.
Dandre Allison

45

Nếu bạn muốn đưa ra các giá trị số nguyên của mình, bạn có thể sử dụng cấu trúc như bên dưới

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 vì nó làm nổi bật thực tế, rằng các giá trị không phải liên tiếp.
rhavin

Ngoài ra, đây dường như là câu trả lời duy nhất hoạt động nếu bạn muốn một tập hợp các giá trị số nguyên thưa thớt (hoặc lặp đi lặp lại) thay vì sử dụng các lệnh mặc định từ 0 .. (đếm-1). Điều đó có thể quan trọng nếu bạn tương tác với mã hiện có, chẳng hạn như qua mạng.
benkc

Đáng để chỉ ra rằng như trong các câu trả lời ở trên, bộ nhớ đệm kết quả của các giá trị () có thể đáng giá, để tránh việc cấp phát bộ nhớ và phân mảng mỗi khi bạn gọi nó.
benkc

1
Và, tùy thuộc vào độ dài của enum của bạn, bạn có thể muốn tạo HashMap hoặc sử dụng tìm kiếm nhị phân hoặc một cái gì đó cho tra cứu này, thay vì thực hiện tìm kiếm tuyến tính mỗi lần.
benkc

2
Đây phải là cách đúng đắn và thực tiễn tốt nhất để chuyển đổi int thành enum và tôi nghĩ bạn có thể đơn giản hóa vấn đề bằng cách tĩnh A GetValue (int _id) {for (A a: A.values ​​() {if (a.getId ( ) == _ id) {return a;}} return null;} Loại bỏ các thứ Không, isEmpty () và so sánh ().
Chris.Zou

35

Bạn có thể thử như thế này.
Tạo Class với id phần tử.

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

Bây giờ Lấy Enum này bằng id dưới dạng int.

MyEnum myEnum = MyEnum.fromId(5);

19

Tôi lưu trữ các giá trị và tạo một phương thức truy cập tĩnh đơn giản:

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
Đây là giải pháp tôi sử dụng bây giờ. Nhưng IMHO sẽ ít gây nhầm lẫn hơn nếu bạn không đặt trường valuescùng tên với phương thức values(). Tôi sử dụng cachedValuescho tên trường.
ToolmakerSteve

2
Hiệu quả và thanh lịch; sao chép và dán vào dự án của tôi :) Điều duy nhất tôi đã thay đổi là fromInt (int i), mà tôi vừa gọi từ (int i) bởi vì nó hơi thừa để có int hai lần trong chữ ký.
pipedreambomb

1
Tại sao không khởi tạo từ đầu? Tại sao chờ đợi cú đánh đầu tiên?
kaiser

9

Các enum Java không có cùng kiểu ánh xạ enum-int như chúng làm trong C ++.

Điều đó nói rằng, tất cả các valuesenum đều có một phương thức trả về một mảng các giá trị enum có thể, vì vậy

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

nên làm việc. Hơi khó chịu một chút và tốt hơn hết là đừng thử và chuyển đổi từ ints sang Enums (hoặc ngược lại) nếu có thể.


9

Đây không phải là điều thường được thực hiện, vì vậy tôi sẽ xem xét lại. Nhưng có nói rằng, các hoạt động cơ bản là: int -> enum bằng EnumType.values ​​() [intNum] và enum -> int bằng enumInst.ordinal ().

Tuy nhiên, vì mọi cách triển khai các giá trị () không có lựa chọn nào khác ngoài việc cung cấp cho bạn một bản sao của mảng (mảng java không bao giờ chỉ đọc), bạn sẽ được phục vụ tốt hơn bằng cách sử dụng EnumMap để lưu trữ enum -> int maps.


2
Re "Đây không phải là điều thường được thực hiện": Trường hợp phổ biến khi nó hữu ích: enum tương ứng với các giá trị int được lưu trữ trong cơ sở dữ liệu.
ToolmakerSteve

@ToolmakerSteve bạn hoàn toàn đúng khi yêu cầu ánh xạ. Nhưng bạn có muốn để loại mã hóa đó cho một số trình ánh xạ HOẶC hoặc một số bộ công cụ / thư viện không?
Dilum Ranatunga


6

Đây là giải pháp tôi dự định đi cùng. Điều này không chỉ hoạt động với các số nguyên không tuần tự, mà nó còn hoạt động với bất kỳ loại dữ liệu nào khác mà bạn có thể muốn sử dụng làm id cơ bản cho các giá trị enum của mình.

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

Tôi chỉ cần chuyển đổi id thành enums vào những thời điểm cụ thể (khi tải dữ liệu từ một tệp), vì vậy không có lý do gì để tôi giữ Bản đồ trong bộ nhớ mọi lúc. Nếu bạn cần bản đồ có thể truy cập mọi lúc, bạn luôn có thể lưu trữ nó dưới dạng thành viên tĩnh của lớp Enum.


IMHO nếu tôi lo ngại về việc sử dụng bộ nhớ, tôi sẽ tự động tạo HashMap - như @ossys nhưng với mã khác khi bộ đệm là null, sau đó thêm phương thức thứ hai clearCachedValueskhi bạn sử dụng xong (đặt trường riêng trở lại null). Tôi xem xét MyEnum.fromInt(i)dễ hiểu hơn là đi vòng quanh một đối tượng bản đồ.
ToolmakerSteve

6

Trong trường hợp nó giúp người khác, tùy chọn tôi thích, không được liệt kê ở đây, sử dụng chức năng Bản đồ của 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);
    }
}

Với mặc định bạn có thể sử dụng null, bạn có thể throw IllegalArgumentExceptionhoặc bạn fromIntcó thể trả lại Optionalbất kỳ hành vi nào bạn thích.


3
Bạn nên đề cập đến việc bạn đang sử dụng Guava. Hoặc bạn có thể sử dụng các luồng:Map<Integer, MyEnum> reverseLookup = Arrays.stream(MyEnum.values()).collect(Collectors.toMap(MyEnum::getValue, Function.identity()));
shmosel

1
Có thể muốn xác định một getValue()phương pháp cũng.
shmosel

@shmosel Rất tiếc, tôi đã bỏ lỡ chức năng getValue, cảm ơn. Việc gõ các phiên bản chung vào stackoverflow không phải lúc nào cũng được. Đã thêm nhận xét về việc sử dụng Guava với một liên kết. Thích phương pháp ổi để truyền phát.
Chad Befus

3

Bạn có thể lặp lại values()enum và so sánh giá trị nguyên của enum với idnhư dưới đây:

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

Và sử dụng như thế này:
TestEnum x = TestEnum.getEnum(4);//Will return TestEnum.Value4
Tôi hy vọng điều này sẽ giúp;)


3

Dựa trên câu trả lời của @ChadBefus và bình luận @shmosel, tôi khuyên bạn nên sử dụng câu trả lời này. (Tra cứu hiệu quả và hoạt động trên java thuần túy> = 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

Một tùy chọn tốt là tránh chuyển đổi từ intsang enum: ví dụ: nếu bạn cần giá trị tối đa, bạn có thể so sánh x.ordinal () với y.ordinal () và trả lại x hoặc y tương ứng. (Bạn có thể cần phải sắp xếp lại các giá trị của mình để làm cho sự so sánh đó có ý nghĩa.)

Nếu điều đó là không thể, tôi sẽ lưu trữ MyEnum.values()vào một mảng tĩnh.


1
Nếu bạn nhận được int từ DB và muốn chuyển đổi nó thành Enum, mà tôi nghĩ là một nhiệm vụ rất phổ biến, bạn sẽ cần chuyển đổi int -> enum, IMO ..
Yuki Inoue

0

Đây là câu trả lời giống như các bác sĩ nhưng nó cho thấy làm thế nào để loại bỏ vấn đề với các mảng có thể thay đổi. Nếu bạn sử dụng cách tiếp cận này vì dự đoán nhánh trước sẽ có hiệu ứng rất ít đến không và toàn bộ mã chỉ gọi hàm giá trị mảng () có thể thay đổi chỉ một lần. Vì cả hai biến đều tĩnh nên chúng sẽ không tiêu thụ bộ nhớ n * cho mỗi lần sử dụng phép liệt kê này.

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 Tôi tin rằng các ngoại lệ tốt hơn đáng kể nếu ngoài giới hạn là rất ít.
Zoso

Niềm tin của bạn dựa trên điều gì?
shmosel

0

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

Sử dụng:

val status = Status.getStatus(1)!!

0

Đã viết thực hiện này. Nó cho phép thiếu các giá trị, giá trị âm và giữ mã nhất quán. Bản đồ được lưu trữ là tốt. Sử dụng một giao diện và cần 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);
    }
}

Giao diện

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;
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.