Chuyển đổi giá trị số nguyên thành phù hợp với Java Enum


86

Tôi có một enum như thế này:

public enum PcapLinkType {
  DLT_NULL(0)
  DLT_EN10MB(1)
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  DLT_UNKNOWN(-1);
    private final int value;   

    PcapLinkType(int value) {
        this.value= value;
    }
}

Bây giờ tôi nhận được một int từ đầu vào bên ngoài và muốn đầu vào phù hợp - ném một ngoại lệ nếu một giá trị không tồn tại là ok, nhưng tốt hơn là tôi nên đặt nó DLT_UNKNOWN trong trường hợp đó.

int val = in.readInt();
PcapLinkType type = ???; /*convert val to a PcapLinkType */

Câu trả lời:


105

Bạn sẽ cần thực hiện việc này theo cách thủ công, bằng cách thêm aa bản đồ tĩnh trong lớp ánh xạ Số nguyên thành enum, chẳng hạn như

private static final Map<Integer, PcapLinkType> intToTypeMap = new HashMap<Integer, PcapLinkType>();
static {
    for (PcapLinkType type : PcapLinkType.values()) {
        intToTypeMap.put(type.value, type);
    }
}

public static PcapLinkType fromInt(int i) {
    PcapLinkType type = intToTypeMap.get(Integer.valueOf(i));
    if (type == null) 
        return PcapLinkType.DLT_UNKNOWN;
    return type;
}

1
được cập nhật với các đề xuất từ ​​dty, đó là một ý tưởng hay.
MeBigFatGuy

Tôi hy vọng bạn đã chạy mã của tôi thông qua một trình biên dịch trước tiên ... Tôi chỉ làm cho nó ở trên đỉnh đầu của tôi. Tôi biết kỹ thuật hoạt động - tôi đã sử dụng nó ngày hôm qua. Nhưng mã nằm trên một máy khác và máy này không có công cụ dành cho nhà phát triển của tôi.
dty

1
allOf chỉ có sẵn cho các bộ
MeBigFatGuy

1
Ngoài ra, EnumMapsử dụng enum làm phím. Trong trường hợp này, OP muốn các giá trị enums.
DTY

8
Điều này có vẻ như rất nhiều chi phí không cần thiết. Những người thực sự cần loại thao tác này có lẽ cần hiệu suất cao vì họ đang ghi / đọc từ các luồng / ổ cắm, trong trường hợp đó, bộ nhớ đệm của values()(nếu các giá trị enum của bạn là tuần tự) hoặc một switchcâu lệnh đơn giản sẽ đánh bại phương pháp này một cách dễ dàng . Nếu bạn chỉ có một số mục nhập trong Enumđó thì không có ý nghĩa gì khi thêm chi phí của HashMap đơn giản để thuận tiện cho việc không phải cập nhật switchcâu lệnh. Phương pháp này có vẻ thanh lịch hơn, nhưng nó cũng lãng phí.
nghiền nát

30

Có một phương pháp tĩnh values()được ghi nhận, nhưng không phải là nơi mà bạn mong đợi nó: http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html

enum MyEnum {
    FIRST, SECOND, THIRD;
    private static MyEnum[] allValues = values();
    public static MyEnum fromOrdinal(int n) {return allValues[n];}
}

Về nguyên tắc, bạn có thể sử dụng chỉ values()[i], nhưng có tin đồn rằng values()sẽ tạo ra một bản sao của mảng mỗi khi nó được gọi.


9
Theo Joshua Bloch (Sách Java hiệu quả) : Không bao giờ lấy giá trị liên kết với enum từ thứ tự của nó; Việc triển khai của bạn không nên dựa vào thứ tự enums.
stevo.mit

4
Thực hiện những gì? Nếu chúng tôi triển khai một số thuật toán, việc triển khai không nên dựa vào thứ tự enums trừ khi thứ tự đó được lập thành văn bản. Khi chúng ta triển khai chính enum, có thể sử dụng các chi tiết triển khai đó, giống như cách sử dụng các phương thức class-private cũng được.
18446744073709551615

1
Không đồng ý. Tôi tin rằng không bao giờ có nghĩa là bất kể tài liệu. Bạn không nên sử dụng thứ tự ngay cả khi bạn tự triển khai enum. Đó là một mùi hôi và nó dễ bị lỗi. Tôi không phải là một chuyên gia nhưng tôi sẽ không tranh luận với Joshua Bloch :)
stevo.mit

4
@ stevo.mit hãy xem enum java.time.Month mới trong Java 8. Phương thức tĩnh Month.of (int) thực hiện chính xác những gì Joshua Bloch nói rằng bạn "không bao giờ" nên làm. Nó trả về một Tháng dựa trên thứ tự của nó.
Klitos Kyriacou 19/02/15

1
@ stevo.mit Có lệnh enumsenums có thứ tự . (Và cả bitmask enums nữa.) Nói chúng chỉ là "enums" đơn giản là không chính xác. Quyết định sử dụng phương tiện biểu đạt nào phải dựa trên mức độ trừu tượng mà bạn thực hiện. Việc sử dụng chi tiết triển khai (phương tiện biểu đạt từ cấp dưới) hoặc sử dụng giả thiết (phương tiện biểu đạt từ cấp trên) là không chính xác. Đối với " không bao giờ ", trong ngôn ngữ của con người không bao giờ có nghĩa là không bao giờ, bởi vì luôn có một số ngữ cảnh. (Thông thường, trong lập trình ứng dụng, không bao giờ ...) BTW, programering.com/a/MzNxQjMwATM.html
18446744073709551615

14

Bạn sẽ phải tạo một phương thức tĩnh mới, nơi bạn lặp lại PcapLinkType.values ​​() và so sánh:

public static PcapLinkType forCode(int code) {
    for (PcapLinkType typе : PcapLinkType.values()) {
        if (type.getValue() == code) {
            return type;
        }
    }
    return null;
 }

Điều đó sẽ tốt nếu nó được gọi là hiếm khi. Nếu nó được gọi thường xuyên, thì hãy xem cách Maptối ưu hóa do những người khác đề xuất.


4
Có thể đắt nếu gọi nhiều. Xây dựng bản đồ tĩnh có khả năng mang lại chi phí khấu hao tốt hơn.
dty

@dty o (n) với n = 200 - tôi không nghĩ đó là vấn đề
Bozho

7
Đó là một tuyên bố hoàn toàn vô lý mà không có cảm giác về tần suất nó được gọi. Nếu nó được gọi một lần, tốt. Nếu nó được gọi cho mọi gói tin lướt qua trên mạng 10Ge thì việc tạo ra một thuật toán nhanh hơn 200 lần là rất quan trọng. Do đó, lý do tại sao tôi đủ điều kiện tuyên bố của mình với "nếu được gọi là nhiều"
dty 14/03

10

Bạn có thể làm như thế này để tự động đăng ký tất cả chúng vào một tập hợp để sau đó dễ dàng chuyển đổi các số nguyên sang enum tương ứng. (BTW, việc thêm chúng vào bản đồ trong hàm tạo enum không được phép . Thật tuyệt khi học những điều mới ngay cả sau nhiều năm sử dụng Java. :)

public enum PcapLinkType {
    DLT_NULL(0),
    DLT_EN10MB(1),
    DLT_EN3MB(2),
    DLT_AX25(3),
    /*snip, 200 more enums, not always consecutive.*/
    DLT_UNKNOWN(-1);

    private static final Map<Integer, PcapLinkType> typesByValue = new HashMap<Integer, PcapLinkType>();

    static {
        for (PcapLinkType type : PcapLinkType.values()) {
            typesByValue.put(type.value, type);
        }
    }

    private final int value;

    private PcapLinkType(int value) {
        this.value = value;
    }

    public static PcapLinkType forValue(int value) {
        return typesByValue.get(value);
    }
}

1
Đó là những gì bạn nhận được khi kiểm tra kỹ câu trả lời của mình trước khi đăng. ;)
Esko Luontola

10

nếu bạn có enum như thế này

public enum PcapLinkType {
  DLT_NULL(0)
  DLT_EN10MB(1)
  DLT_EN3MB(2),
  DLT_AX25(3),
  DLT_UNKNOWN(-1);

    private final int value;   

    PcapLinkType(int value) {
        this.value= value;
    }
}

sau đó bạn có thể sử dụng nó như

PcapLinkType type = PcapLinkType.values()[1]; /*convert val to a PcapLinkType */

bạn bỏ lỡ comment / * snip, hơn 200 enums, không phải lúc nào liên tiếp * /.
MeBigFatGuy

chỉ trong trường hợp giá trị enum của bạn là độ
nhạy

4

Như @MeBigFatGuy đã nói, ngoại trừ việc bạn có thể tạo static {...} khối cách sử dụng một vòng lặp qua values()bộ sưu tập:

static {
    for (PcapLinkType type : PcapLinkType.values()) {
        intToTypeMap.put(type.getValue(), type);
    }
}

4

Tôi biết câu hỏi này đã có từ vài năm trước, nhưng vì Java 8, trong khi chờ đợi, đã mang lại cho chúng tôi Optional, tôi nghĩ tôi sẽ đưa ra một giải pháp bằng cách sử dụng nó (và StreamCollectors):

public enum PcapLinkType {
  DLT_NULL(0),
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  // DLT_UNKNOWN(-1); // <--- NO LONGER NEEDED

  private final int value;
  private PcapLinkType(int value) { this.value = value; }

  private static final Map<Integer, PcapLinkType> map;
  static {
    map = Arrays.stream(values())
        .collect(Collectors.toMap(e -> e.value, e -> e));
  }

  public static Optional<PcapLinkType> fromInt(int value) {
    return Optional.ofNullable(map.get(value));
  }
}

Optionalgiống như null: nó đại diện cho một trường hợp khi không có giá trị (hợp lệ). Nhưng nó là một sự thay thế an toàn hơn cho kiểu nullhoặc một giá trị mặc định, chẳng hạn như DLT_UNKNOWNvì bạn có thể quên kiểm tra nullhoặc DLT_UNKNOWNcác trường hợp. Cả hai đều là PcapLinkTypegiá trị hợp lệ ! Ngược lại, bạn không thể gán Optional<PcapLinkType>giá trị cho biến kiểu PcapLinkType. Optionalkhiến bạn kiểm tra giá trị hợp lệ trước.

Tất nhiên, nếu bạn muốn giữ lại DLT_UNKNOWNđể tương thích ngược hoặc bất kỳ lý do nào khác, bạn vẫn có thể sử dụng Optionalngay cả trong trường hợp đó, sử dụng orElse()để chỉ định nó làm giá trị mặc định:

public enum PcapLinkType {
  DLT_NULL(0),
  DLT_EN3MB(2),
  DLT_AX25(3),
  /*snip, 200 more enums, not always consecutive.*/
  DLT_UNKNOWN(-1);

  private final int value;
  private PcapLinkType(int value) { this.value = value; }

  private static final Map<Integer, PcapLinkType> map;
  static {
    map = Arrays.stream(values())
        .collect(Collectors.toMap(e -> e.value, e -> e));
  }

  public static PcapLinkType fromInt(int value) {
    return Optional.ofNullable(map.get(value)).orElse(DLT_UNKNOWN);
  }
}

3

Bạn có thể thêm một phương thức tĩnh trong enum của mình để chấp nhận intmột tham số và trả về a PcapLinkType.

public static PcapLinkType of(int linkType) {

    switch (linkType) {
        case -1: return DLT_UNKNOWN
        case 0: return DLT_NULL;

        //ETC....

        default: return null;

    }
}

Tốt hơn là đừng quên thêm một mục vào switchcâu lệnh đó nếu bạn thêm một enum mới. Không lý tưởng, IMHO.
dty

1
@dty Vì vậy, bạn nghĩ chi phí của một HashMap lớn hơn nhu cầu thêm một trường hợp mới vào câu lệnh switch?
nghiền nát

1
Tôi nghĩ tôi muốn viết mã giúp tôi không mắc lỗi và do đó có nhiều khả năng đúng hơn trước khi tôi tập trung vào hiệu suất vi mô của tra cứu băm.
dty

3

Đây là những gì tôi sử dụng:

public enum Quality {ENOUGH,BETTER,BEST;
                     private static final int amount = EnumSet.allOf(Quality.class).size();
                     private static Quality[] val = new Quality[amount];
                     static{ for(Quality q:EnumSet.allOf(Quality.class)){ val[q.ordinal()]=q; } }
                     public static Quality fromInt(int i) { return val[i]; }
                     public Quality next() { return fromInt((ordinal()+1)%amount); }
                    }

Nói chung, sử dụng thứ tự đã được xác định là thực hành xấu, tốt hơn là nên tránh.
Rafael

1
static final PcapLinkType[] values  = { DLT_NULL, DLT_EN10MB, DLT_EN3MB, null ...}    

...

public static PcapLinkType  getPcapLinkTypeForInt(int num){    
    try{    
       return values[int];    
    }catch(ArrayIndexOutOfBoundsException e){    
       return DLT_UKNOWN;    
    }    
}    

1
Đắt nếu gọi là nhiều. Cần nhớ cập nhật mảng (tại sao bạn thậm chí có nó khi enums xác định một .values()phương thức?).
dty

@dty có phải là thử / bắt không? Tôi nghĩ sẽ công bằng hơn nếu nói nó đắt nếu nhiều giá trị thuộc danh mục DLT_UNKNOWN.
nsfyn55

1
Tôi thực sự ngạc nhiên khi thấy một giải pháp mảng được bình chọn và một giải pháp bản đồ được bình chọn. Điều tôi không thích ở đây là --int, nhưng rõ ràng đó là lỗi đánh máy.
18446744073709551615

Tôi hiểu rồi: họ muốn nullthay DLT_UKNOWN:)
18446744073709551615

1
Tại sao không static final values[] = PcapLinkType.values()?
18446744073709551615

0

Không có cách nào để xử lý các kiểu liệt kê dựa trên số nguyên một cách thanh lịch. Bạn có thể nghĩ đến việc sử dụng kiểu liệt kê dựa trên chuỗi thay vì giải pháp của mình. Không phải là một cách ưa thích mọi lúc, nhưng nó vẫn tồn tại.

public enum Port {
  /**
   * The default port for the push server.
   */
  DEFAULT("443"),

  /**
   * The alternative port that can be used to bypass firewall checks
   * made to the default <i>HTTPS</i> port.
   */
  ALTERNATIVE("2197");

  private final String portString;

  Port(final String portString) {
    this.portString = portString;
  }

  /**
   * Returns the port for given {@link Port} enumeration value.
   * @return The port of the push server host.
   */
  public Integer toInteger() {
    return Integer.parseInt(portString);
  }
}
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.