Chuyển đổi từ enum thứ tự sang loại enum


316

Tôi đã loại enum ReportTypeEnumđược truyền giữa các phương thức trong tất cả các lớp của mình nhưng sau đó tôi cần truyền cái này vào URL để tôi sử dụng phương thức thứ tự để lấy giá trị int. Sau khi tôi nhận được nó trong trang JSP khác của mình, tôi cần chuyển đổi nó trở lại để ReportTypeEnumtôi có thể tiếp tục chuyển nó.

Làm thế nào tôi có thể chuyển đổi thứ tự sang ReportTypeEnum?

Sử dụng Java 6 SE.


1
Cho đến nay không có Java 6 EE (AFAIK). Có Java SE 6 và Java EE 5.
Hosam Aly

Câu trả lời:


632

Để chuyển đổi một thứ tự thành sự phục hồi enum của nó, bạn có thể muốn làm điều này:

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

Xin lưu ý các giới hạn mảng.

Lưu ý rằng mọi cuộc gọi để values()trả về một mảng mới được nhân bản có thể ảnh hưởng đến hiệu suất theo cách tiêu cực. Bạn có thể muốn lưu trữ mảng nếu nó sẽ được gọi thường xuyên.

Mã ví dụ về cách lưu trữvalues() .


Câu trả lời này đã được chỉnh sửa để bao gồm các phản hồi được đưa ra bên trong các bình luận


Tôi đã thực hiện giải pháp này và nó không hiệu quả với tôi. Nó trả về giá trị thứ tự không được đảm bảo để khớp với thứ tự mà các kiểu liệt kê được thêm vào. Tôi không biết đó là những gì câu trả lời này đang ủng hộ, nhưng tôi muốn cảnh báo mọi người dù sao
IcedDante

@IcesDante: thứ tự chắc chắn được đảm bảo tương ứng với thứ tự của các giá trị liệt kê trong nguồn. Nếu bạn quan sát một hành vi khác, thì một cái gì đó phải sai. Câu trả lời của tôi ở trên là tối ưu cho tất cả các lý do được nêu trong các câu trả lời khác.
Joachim Sauer

@JoachimSauer Có lẽ IcedDante có nghĩa là thứ tự có thể không khớp nếu nó được sản xuất và lưu bởi một phiên bản trước đó của nguồn, trong đó các giá trị enum theo thứ tự khác.
LarsH

137

Đây gần như chắc chắn là một ý tưởng tồi . Chắc chắn nếu thứ tự là de-facto tiếp tục tồn tại (ví dụ bởi vì ai đó đã đánh dấu URL) - nó có nghĩa là bạn phải luôn giữ gìnenum thứ tự trong tương lai, điều này có thể không rõ ràng đối với những người duy trì mã xuống dòng.

Tại sao không mã hóa enumbằng cách sử dụng myEnumValue.name()(và giải mã thông qua ReportTypeEnum.valueOf(s)) thay vào đó?


24
Điều gì nếu bạn thay đổi tên của enum (nhưng vẫn giữ thứ tự)?
Arne Evertsson

6
@Arne - Tôi nghĩ rằng điều này ít có khả năng hơn một số người thiếu kinh nghiệm đi cùng và thêm một vị valuetrí bắt đầu hoặc vị trí bảng chữ cái / logic chính xác của nó. (Theo logic tôi có nghĩa là các TimeUnitgiá trị ví dụ có vị trí logic)
oxbow_lakes

7
Tôi chắc chắn thích buộc thứ tự enum hơn là tên enum của tôi ... đây là lý do tại sao tôi thích lưu trữ thứ tự hơn là tên của enum trong cơ sở dữ liệu. Hơn nữa, tốt hơn là sử dụng thao tác int thay vì Chuỗi ...

8
Tôi đồng ý. Trong API công khai, việc thay đổi tên của Enum sẽ phá vỡ tính tương thích ngược nhưng thay đổi thứ tự thì không. Vì lý do đó, sẽ hợp lý hơn khi sử dụng tên này làm "chìa khóa" của bạn
Noel

3
Lưu trữ thứ tự làm cho việc dịch ý tưởng của bạn sang các ngôn ngữ khác dễ dàng hơn. Nếu bạn cần viết một số thành phần trong C thì sao?
QED

94

Nếu tôi sẽ sử dụng values()nhiều:

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

Trong khi đó any.java:

Suit suit = Suit.values[ordinal];

Tâm trí giới hạn mảng của bạn.


3
+1 đây là giải pháp tốt nhất IMHO bởi vì người ta có thể vượt qua các mệnh lệnh xung quanh đặc biệt là trong android.os.Message.
likejudo

9
Điều này rất đáng lo ngại vì các mảng có thể thay đổi . Mặc dù các giá trị [] là cuối cùng, nhưng nó không ngăn chặnSuit.values[0] = Suit.Diamonds; một nơi nào đó trong mã của bạn. Lý tưởng là điều đó sẽ không bao giờ xảy ra, nhưng nguyên tắc chung là không phơi bày các trường có thể thay đổi vẫn còn tồn tại. Đối với phương pháp này, xem xét sử dụng Collections.unmodifiableListhoặc tương tự thay thế.
Mshnik

Làm thế nào về - riêng tư tĩnh cuối cùng giá trị [] = value (); công khai tĩnh Suit [] getValues ​​() {trả về giá trị; }
Pratyush

4
@Pratyush sẽ làm cho biến mảng không thay đổi, nhưng không phải là nội dung của nó. Tôi vẫn có thể làm getValues ​​() [0] = SomethingElse;
Calabacin

Đồng ý, do đó, điểm không được phơi bày Suit values[]trực tiếp hoặc gián tiếp (làm thế nào được đề cập thông quagetValues() ), tôi làm việc xung quanh với một phương thức công khai trong đó ordinalgiá trị phải được gửi như thế nào một đối số và trả về Suitđại diện từ đó Suit values[]. Điểm ở đây (gạch câu hỏi ngay từ đầu) đã tạo ra một loại enum từ một giáo sĩ enum
Manuel Jordan

13

Tôi đồng ý với hầu hết mọi người rằng sử dụng thứ tự có lẽ là một ý tưởng tồi. Tôi thường giải quyết vấn đề này bằng cách cung cấp cho enum một hàm tạo riêng có thể lấy ví dụ giá trị DB sau đó tạo một fromDbValuehàm tĩnh tương tự như trong câu trả lời của Jan.

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

}

Bây giờ bạn có thể thay đổi thứ tự mà không thay đổi tra cứu và ngược lại.


Đây là câu trả lời đúng. Tôi ngạc nhiên khi nó có rất ít điểm so với các câu trả lời trực tiếp nhưng có khả năng không hợp lệ khác (do thay đổi mã trong tương lai).
Calabacin

8

Bạn có thể sử dụng bảng tra cứu tĩnh:

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
Xem thêm Enums .
thùng rác

11
Ồ Chỉ là wow! Điều này là gọn gàng, tất nhiên, nhưng ... bạn biết đấy - lập trình viên C trong tôi hét lên đau đớn khi thấy bạn phân bổ một HashMap đầy đủ và thực hiện tra cứu bên trong nó CHỈ để quản lý 4 hằng số: spades, heart, kim cương và câu lạc bộ! Lập trình viên AC sẽ phân bổ 1 byte cho mỗi: 'const char CLUBS = 0;' v.v ... Vâng, tra cứu HashMap là O (1), nhưng chi phí bộ nhớ và CPU của HashMap, trong trường hợp này làm cho nó có nhiều đơn đặt hàng có cường độ chậm hơn và đói tài nguyên hơn là gọi trực tiếp .values ​​()! Không có gì ngạc nhiên khi Java là một con heo nhớ như vậy nếu mọi người viết như thế này ...
Leszek

2
Không phải mọi chương trình đều yêu cầu hiệu suất của trò chơi ba chữ A. Trong nhiều trường hợp, giao dịch bộ nhớ và CPU để đảm bảo an toàn, khả năng đọc, khả năng bảo trì, hỗ trợ đa nền tảng, thu gom rác, v.v ... là điều hợp lý. Ngôn ngữ cấp cao hơn tồn tại vì một lý do.
Ngày 1 tháng

3
Nhưng nếu phạm vi khóa của bạn luôn luôn 0...(n-1), thì một mảng sẽ ít mã hơn và cũng dễ đọc hơn; tăng hiệu suất chỉ là một phần thưởng. private static final Suit[] VALUES = values();public Suit fromOrdinal(int ordinal) { return VALUES[ordinal]; }. Lợi thế bổ sung: sự cố ngay lập tức trên các lệnh không hợp lệ, thay vì âm thầm trả về null. (Không phải lúc nào cũng là một lợi thế. Nhưng thường xuyên.)
Thomas

4

Đây là những gì tôi sử dụng. Tôi không giả vờ rằng nó "kém hiệu quả" hơn nhiều so với các giải pháp đơn giản ở trên. Những gì nó làm là cung cấp một thông báo ngoại lệ rõ ràng hơn nhiều so với "ArrayIndexOutOfBound" khi giá trị thứ tự không hợp lệ được sử dụng trong giải pháp ở trên.

Nó sử dụng thực tế là Enumset javadoc chỉ định iterator trả về các phần tử theo thứ tự tự nhiên của chúng. Có một khẳng định nếu điều đó không chính xác.

Thử nghiệm JUnit4 cho thấy nó được sử dụng như thế nào.

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

Đây là những gì tôi làm trên Android với 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];
    }
}

nó ngắn và tôi không cần phải lo lắng về việc có ngoại lệ trong Proguard


1

Bạn có thể định nghĩa một phương thức đơn giản như:

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

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

Và sử dụng nó như:

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

lớp kiểm tra

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

Tôi đánh giá cao việc bạn chia sẻ với chúng tôi nếu bạn có một mã hiệu quả hơn, enum của tôi rất lớn và liên tục được gọi hàng ngàn lần.


Tôi nghĩ bạn có nghĩa là "nếu (thứ tự <1 | | thứ tự> 4) trả về null;"
geowar

0

Vì vậy, một cách là làm việc ExampleEnum valueOfOrdinal = ExampleEnum.values()[ordinal];nào hiệu quả và dễ dàng, tuy nhiên, như đã đề cập trước đó, ExampleEnum.values()trả về một mảng nhân bản mới cho mỗi cuộc gọi. Điều đó có thể tốn kém không cần thiết. Chúng ta có thể giải quyết điều đó bằng cách lưu trữ các mảng như vậy ExampleEnum[] values = values(). Nó cũng "nguy hiểm" khi cho phép sửa đổi mảng bộ nhớ cache của chúng tôi. Ai đó có thể viết ExampleEnum.values[0] = ExampleEnum.type2;Vì vậy tôi sẽ đặt nó ở chế độ riêng tư bằng phương thức truy cập không sao chép thêm.

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

Bạn sẽ sử dụng ExampleEnum.value(ordinal)để có được giá trị enum liên quan đếnordinal


-1

Mỗi enum có tên (), cung cấp một chuỗi với tên của thành viên enum.

Cho enum Suit{Heart, Spade, Club, Diamond}, Suit.Heart.name()sẽ choHeart .

Mỗi enum có một valueOf() phương thức, lấy một kiểu enum và một chuỗi, để thực hiện thao tác ngược lại:

Enum.valueOf(Suit.class, "Heart")trả lại Suit.Heart.

Tại sao bất cứ ai sẽ sử dụng chức vụ là ngoài tôi. Nó có thể nhanh hơn nano giây, nhưng không an toàn, nếu các thành viên enum thay đổi, vì một nhà phát triển khác có thể không nhận thức được một số mã đang dựa vào các giá trị thứ tự (đặc biệt là trong trang JSP được trích dẫn trong câu hỏi, mạng và cơ sở dữ liệu hoàn toàn chi phối thời gian, không sử dụng một số nguyên trên một chuỗi).


2
Vì so sánh số nguyên nhanh hơn nhiều so với so sánh chuỗi?
HighCommander4

2
Nhưng các lệnh sẽ thay đổi nếu ai đó sửa đổi enum (Thêm / sắp xếp lại các bộ nhớ). Đôi khi, đó là về sự an toàn và không phải tốc độ, đặc biệt là trên trang JSP có độ trễ mạng gấp 1.000.000 lần so với việc so sánh một mảng các số nguyên (một chuỗi) và một số nguyên.
Tony BenBrahim

toString có thể bị ghi đè, vì vậy nó có thể không trả về tên enum. Tên phương thức () là tên của enum (cuối cùng)
gerardw

nhận xét mã và phiên bản ứng dụng cũng là một điều (có thể định dạng tệp đơn giản hơn và nhỏ hơn theo cách này)
joe pelletier

Không chắc chắn lý do tại sao điều này bị hạ cấp khi về cơ bản nó đề xuất điều tương tự như trong câu trả lời của oxbow_lakes. Chắc chắn an toàn hơn so với sử dụng thứ tự.
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.