แปลงค่าจำนวนเต็มเพื่อจับคู่ Java Enum


87

ฉันมีสิ่งนี้:

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

ตอนนี้ฉันได้รับ int จากอินพุตภายนอกและต้องการอินพุตที่ตรงกัน - การยกเว้นหากไม่มีค่าก็โอเค แต่ควรมีไว้DLT_UNKNOWN ในกรณีนั้น

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

คำตอบ:


106

คุณจะต้องทำสิ่งนี้ด้วยตนเองโดยการเพิ่มแผนที่แบบคงที่ในคลาสที่แมปจำนวนเต็มกับ enums เช่น

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
อัปเดตพร้อมคำแนะนำจาก dty ซึ่งเป็นความคิดที่ดี
MeBigFatGuy

ฉันหวังว่าคุณจะรันโค้ดของฉันผ่านคอมไพเลอร์ก่อน ... ฉันเพิ่งสร้างมันขึ้นมาจากด้านบนของหัว ฉันรู้ว่าเทคนิคนี้ใช้ได้ผล - ฉันใช้มันเมื่อวานนี้ แต่รหัสอยู่ในเครื่องอื่นและอันนี้ไม่มีเครื่องมือ dev ของฉัน
DTY

1
allOf ใช้ได้เฉพาะชุด
MeBigFatGuy

1
นอกจากนี้ยังEnumMapใช้ enums เป็นกุญแจ ในกรณีนี้ OP ต้องการให้ enums เป็นค่า
DTY

8
ดูเหมือนว่าจะมีค่าใช้จ่ายที่ไม่จำเป็นมากมาย ผู้ที่ต้องการการดำเนินการประเภทนี้อาจต้องการประสิทธิภาพสูงเนื่องจากกำลังเขียน / อ่านจากสตรีม / ซ็อกเก็ตซึ่งในกรณีนี้การแคชvalues()(ถ้าค่า enum ของคุณเป็นลำดับ) หรือswitchคำสั่งง่ายๆจะเอาชนะวิธีนี้ได้อย่างคล่องแคล่ว . หากคุณมีรายการเพียงไม่กี่รายการในของคุณEnumการเพิ่มค่าใช้จ่ายของ HashMap เพียงเพื่อความสะดวกในการไม่ต้องอัปเดตswitchคำสั่ง วิธีนี้อาจดูหรูหรากว่า แต่ก็สิ้นเปลืองเช่นกัน
ขยี้

30

มีวิธีการแบบคงvalues()ที่ซึ่งมีการบันทึกไว้ แต่ไม่ใช่ที่ที่คุณคาดหวัง: 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];}
}

โดยหลักการแล้วคุณสามารถใช้เพียงแค่values()[i]แต่มีข่าวลือว่าvalues()จะสร้างสำเนาของอาร์เรย์ทุกครั้งที่มีการเรียกใช้


9
อ้างอิงจาก Joshua Bloch (Effective Java Book) : อย่าได้รับค่าที่เกี่ยวข้องกับ enum จากลำดับของมัน การติดตั้งของคุณไม่ควรขึ้นอยู่กับคำสั่ง enums
stevo.mit

4
การดำเนินการของอะไร? หากเราใช้อัลกอริทึมบางอย่างการใช้งานไม่ควรขึ้นอยู่กับคำสั่ง enums เว้นแต่คำสั่งนั้นจะได้รับการบันทึกไว้ เมื่อเราใช้ enum เองก็สามารถใช้รายละเอียดการใช้งานดังกล่าวในลักษณะเดียวกับที่สามารถใช้วิธีการส่วนตัวแบบคลาสได้
18446744073709551615

1
ไม่เห็นด้วย. ฉันเชื่อว่าไม่เคยมีความหมายโดยไม่คำนึงถึงเอกสาร คุณไม่ควรใช้ลำดับแม้ว่าคุณจะใช้ enum ด้วยตัวเองก็ตาม มีกลิ่นเหม็นและเกิดข้อผิดพลาดได้ง่าย ฉันไม่ใช่ผู้เชี่ยวชาญ แต่ฉันจะไม่เถียงกับ Joshua Bloch :)
stevo.mit

4
@ stevo.mit ดู enum java.time.Month ใหม่ใน Java 8 วิธีการแบบคงที่ Month.of (int) ทำในสิ่งที่ Joshua Bloch บอกว่าคุณไม่ควร "ไม่ควรทำ" ส่งคืนเดือนตามลำดับ
Klitos Kyriacou

1
@ stevo.mit มีคำสั่งให้ enumsและenums เรียงลำดับ (และbitmask enumsด้วย) มันไม่ถูกต้องที่จะพูดถึงพวกเขาในฐานะ "enums" การตัดสินใจว่าจะใช้วิธีการแสดงออกอย่างไรต้องขึ้นอยู่กับระดับของนามธรรมที่คุณทำงาน เป็นเรื่องที่ไม่ถูกต้องในการใช้รายละเอียดการนำไปใช้งาน (วิธีการที่แสดงออกจากระดับล่าง) หรือสมมติฐานการใช้งาน (วิธีการแสดงออกจากระดับที่สูงกว่า) ในฐานะที่เป็น " ไม่ " ในภาษามนุษย์ไม่เคยไม่เคยไม่เคยหมายถึงเพราะมีเสมอบางบริบท (โดยปกติในการเขียนโปรแกรมแอปพลิเคชันไม่เคย ... ) BTW, programering.com/a/MzNxQjMwATM.html
18446744073709551615

16

คุณจะต้องสร้างวิธีการแบบคงที่ใหม่โดยที่คุณทำซ้ำ PcapLinkType.values ​​() และเปรียบเทียบ:

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

จะดีถ้าเรียกว่าไม่ค่อย หากมีการเรียกใช้บ่อยให้ดูที่การMapเพิ่มประสิทธิภาพที่แนะนำโดยผู้อื่น


4
อาจจะแพงถ้าเรียกเยอะ. การสร้างแผนที่คงที่มีแนวโน้มที่จะให้ต้นทุนตัดจำหน่ายที่ดีกว่า
DTY

@dty o (n) กับ n = 200 - ฉันไม่คิดว่ามันเป็นปัญหา
Bozho

7
นั่นเป็นคำพูดที่ไร้สาระโดยไม่รู้สึกว่าถูกเรียกบ่อยแค่ไหน ถ้าเรียกครั้งเดียวก็ดีนะ หากมีการเรียกทุกแพ็กเก็ตที่ผ่านมาบนเครือข่าย 10Ge การทำให้อัลกอริทึมเร็วขึ้น 200 เท่านั้นสำคัญมาก ด้วยเหตุนี้ฉันจึงรับรองคำสั่งของฉันด้วย "ถ้าเรียกมาก"
dty

10

คุณสามารถทำสิ่งนี้เพื่อลงทะเบียนทั้งหมดลงในคอลเลกชันโดยอัตโนมัติซึ่งจะแปลงจำนวนเต็มเป็น enum ที่เกี่ยวข้องได้อย่างง่ายดาย ( ไม่อนุญาตให้เพิ่ม BTW ลงในแผนที่ใน enum constructor ยินดีที่ได้เรียนรู้สิ่งใหม่ ๆ แม้จะใช้ 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
นั่นคือสิ่งที่คุณจะได้รับจากการตรวจสอบคำตอบของคุณอีกครั้งก่อนโพสต์ ;)
Esko Luontola

10

ถ้าคุณมี enum แบบนี้

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

แล้วคุณสามารถใช้มันเช่น

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

คุณพลาดความคิดเห็น / * snip อีก 200 enums ไม่ติดต่อกันเสมอไป * /
MeBigFatGuy

ในกรณีที่ค่า enum ของคุณคือการเปลี่ยนผ่านจาก Zero นี่เป็นการปฏิบัติที่ไม่ดี
cuasodayleo

4

ดังที่ @MeBigFatGuy กล่าวยกเว้นว่าคุณสามารถทำให้static {...}บล็อกของคุณใช้การวนซ้ำในvalues()คอลเล็กชัน:

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

4

ฉันรู้ว่าคำถามนี้มีอายุไม่กี่ปี แต่ในขณะที่ Java 8 มีมาให้เราOptionalฉันคิดว่าฉันจะเสนอวิธีแก้ปัญหาโดยใช้ (และStreamและCollectors):

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

Optionalเป็นเหมือนnull: มันแสดงถึงกรณีที่ไม่มีค่า (ถูกต้อง) แต่เป็นทางเลือกที่ปลอดภัยกว่าnullหรือเป็นค่าเริ่มต้นเช่นDLT_UNKNOWNคุณอาจลืมตรวจสอบnullหรือDLT_UNKNOWNกรณี เป็นPcapLinkTypeค่าที่ใช้ได้ทั้งคู่! ในทางตรงกันข้ามคุณไม่สามารถกำหนดOptional<PcapLinkType>ค่าให้กับตัวแปรประเภทPcapLinkTypeได้ Optionalทำให้คุณตรวจสอบค่าที่ถูกต้องก่อน

แน่นอนว่าหากคุณต้องการคงไว้DLT_UNKNOWNเพื่อความเข้ากันได้แบบย้อนหลังหรือเหตุผลอื่นใดคุณยังคงสามารถใช้ได้Optionalแม้ในกรณีนั้นโดยใช้orElse()เพื่อระบุเป็นค่าเริ่มต้น:

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

คุณสามารถเพิ่มวิธีการแบบคงที่ใน enum ของคุณที่ยอมรับintเป็นพารามิเตอร์และส่งกลับ a PcapLinkType.

public static PcapLinkType of(int linkType) {

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

        //ETC....

        default: return null;

    }
}

อย่าลืมเพิ่มรายการในswitchคำสั่งนั้นหากคุณเพิ่ม enum ใหม่ ไม่เหมาะ IMHO
DTY

1
@dty คุณคิดว่าค่าใช้จ่ายของ HashMap มีมากกว่าความจำเป็นในการเพิ่มกรณีใหม่ในคำสั่ง switch หรือไม่?
ขยี้

1
ฉันคิดว่าฉันควรจะเขียนโค้ดที่ช่วยให้ฉันไม่ทำผิดพลาดและมีแนวโน้มที่จะถูกต้องมากกว่าก่อนที่ฉันจะมุ่งเน้นไปที่ประสิทธิภาพระดับไมโครของการค้นหาแฮช
DTY

3

นี่คือสิ่งที่ฉันใช้:

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

การใช้ลำดับได้รับการระบุว่าเป็นการปฏิบัติที่ไม่ดีโดยทั่วไปควรหลีกเลี่ยง
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
แพงถ้าเรียกเยอะ. อย่าลืมอัปเดตอาร์เรย์ (ทำไมคุณถึงมีเมื่อ enums กำหนด.values()วิธีการ?)
DTY

@dty เป็นการลอง / จับหรือไม่? ฉันคิดว่ามันจะยุติธรรมกว่าที่จะบอกว่ามันแพงถ้าค่าจำนวนมากอยู่ในหมวดหมู่ DLT_UNKNOWN
nsfyn55

1
ฉันประหลาดใจมากที่เห็นโซลูชันอาร์เรย์ถูกโหวตลงและโซลูชันแผนที่ได้รับการโหวต สิ่งที่ฉันไม่ชอบที่นี่คือ--intแต่เห็นได้ชัดว่าเป็นการพิมพ์ผิด
18446744073709551615

ฉันเห็น: พวกเขาต้องการnullแทนที่DLT_UKNOWN:)
18446744073709551615

1
ทำไมไม่static final values[] = PcapLinkType.values()?
18446744073709551615

0

ไม่มีวิธีจัดการกับประเภทการแจกแจงตามจำนวนเต็มอย่างหรูหรา คุณอาจนึกถึงการใช้การแจงนับตามสตริงแทนโซลูชันของคุณ ไม่ใช่วิธีที่ต้องการตลอดเวลา แต่ก็ยังคงมีอยู่

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