รับ enum ที่เกี่ยวข้องกับค่า int


89

ก่อนหน้านี้ฉันมี LegNo enums ของฉันที่กำหนดไว้ง่ายๆว่า:

NO_LEG, LEG_ONE, LEG_TWO

และด้วยการโทรreturn LegNo.values()[i];ฉันสามารถรับค่าที่เกี่ยวข้องกับแต่ละ enum

แต่ตอนนี้ฉันตัดสินใจแล้วว่าฉันต้องการให้LegNoenum NO_LEGเป็น int -1 แทนที่จะเป็น 0 ดังนั้นฉันจึงตัดสินใจใช้ตัวสร้างส่วนตัวเพื่อเริ่มต้นและตั้งค่า int

NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

private LegNo(final int leg) { legNo = leg; }

สิ่งเดียวตอนนี้ก็คือเพราะฉันทำแบบนี้วิธีนี้values()จะไม่ได้ผลกับNO_LEGenum ฉันจะรับ enum ที่เกี่ยวข้องกับ int ได้อย่างไร มีวิธีใดที่มีประสิทธิภาพในการทำสิ่งนี้นอกเหนือจากการใช้คำสั่ง case switch หรือ if-elseif-elseif

ฉันเห็นคำถาม SO มากมายเกี่ยวกับการรับค่า int จาก enum แต่ฉันกลับหลัง

คำตอบ:


149

แก้ไขสิงหาคม 2561

วันนี้ฉันจะใช้สิ่งนี้ดังนี้

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int value;

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

    public static Optional<LegNo> valueOf(int value) {
        return Arrays.stream(values())
            .filter(legNo -> legNo.value == value)
            .findFirst();
    }
}

คุณจะต้องรักษาการทำแผนที่ไว้ใน enum

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private static Map<Integer, LegNo> map = new HashMap<Integer, LegNo>();

    static {
        for (LegNo legEnum : LegNo.values()) {
            map.put(legEnum.legNo, legEnum);
        }
    }

    private LegNo(final int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

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

แก้ไข: เปลี่ยนชื่อวิธีการvalueOfเป็นแบบอินไลน์กับคลาส Java อื่น ๆ


ขอโทษฉันไม่แน่ใจว่าฉันชัดเจนเพียงพอหรือไม่ ฉันต้องการส่งผ่านใน int และรับ enum ที่เกี่ยวข้อง
L-Samuels

@ L-Samuels เดาว่าฉันอ่านคำถามของคุณไม่ถูกต้อง ดูการอัปเดตของฉัน
adarshr

2
ฉันรู้ว่านี้ดูเหมือนชัดเจน LegNo foo = LegNo.valueOf(2);แต่ใช้ต้องการเพื่อ: รหัสก่อนหน้านี้จะส่งคืนไฟล์LegNo.LEG_TWO.
FirstOne

1
หากต้องการสังเกตการส่งค่าจำนวนเต็มที่ไม่ถูกต้อง (ไม่ได้แมป) จะส่งคืนnullตามที่คาดไว้โดยใช้HashMap.get : ส่งคืนค่าที่คีย์ที่ระบุถูกแมปหรือเป็นค่าว่างหากแมปนี้ไม่มีการแมปสำหรับคีย์
FirstOne

แม้ว่าไวยากรณ์ของสตรีมจะเรียบร้อย แต่ก็ควรชี้ให้เห็นว่ามีความซับซ้อนของเวลาสูงกว่าแผนที่แบบคงที่ (ซึ่งยอมรับว่ามีการใช้หน่วยความจำสูงกว่า) ไม่ใช่ปัญหาสำหรับ 3 ค่า แต่เป็นเรื่องที่น่ากังวลหากคุณvalueOf()มีสมาชิก 1,000 คนอยู่ในวงอื่น
Patrick M

24

คุณยังสามารถรวมวิธีการแบบคงที่ใน enum ซึ่งวนซ้ำผ่านสมาชิกทั้งหมดและส่งกลับค่าที่ถูกต้อง

public enum LegNo {
   NO_LEG(-1),
   LEG_ONE(1),
   LEG_TWO(2);

   private int legIndex;

   private LegNo(int legIndex) { this.legIndex = legIndex; }

   public static LegNo getLeg(int legIndex) {
      for (LegNo l : LegNo.values()) {
          if (l.legIndex == legIndex) return l;
      }
      throw new IllegalArgumentException("Leg not found. Amputated?");
   }
}

ตอนนี้ถ้าคุณต้องการรับค่า Enum ด้วยจำนวนเต็มคุณเพียงแค่ใช้:

int myLegIndex = 1; //expected : LEG_ONE
LegNo myLeg = LegNo.getLeg(myLegIndex);

ฉันคิดว่าสิ่งนี้จะสวยงามกว่าการใช้คำสั่ง if else if แต่เนื่องจากมีการค้นหามากขึ้นดังนั้นกลยุทธ์แผนที่ที่แนะนำโดย @adarshr จะดีกว่า แม้ว่าจะโหวตให้กับอารมณ์ขัน
L-Samuels

1
ฉันชอบกลยุทธ์แผนที่มากเช่นกัน โดยเฉพาะอย่างยิ่งเมื่อ enum มีค่ามากมายหรือต้องค้นหาผ่านกลไกนี้บ่อยมาก อย่างไรก็ตามหากการค้นหาค่าด้วย int ที่เกี่ยวข้องเป็นเหตุการณ์ที่เกิดขึ้นได้ยากหรือคุณมี enums ที่แตกต่างกันจำนวนมากที่มีข้อกำหนดการค้นหาเดียวกันฉันเชื่อว่าวิธีของฉันจะเป็นมิตรกับทรัพยากรมากขึ้นเนื่องจากค่าใช้จ่ายสำหรับแผนที่จะถูกบันทึกไว้ นอกจากนี้ฉันพบว่ามันทำให้โค้ดรกน้อยลง ฉันมีบางกรณีการใช้งานที่ฉันจะเปลี่ยนไปใช้ประเภทแผนที่ด้วยตัวเองอย่างแน่นอน
Mike Adler

คุณไม่ควรได้รับค่า enum ที่เกี่ยวข้องตามลำดับของมัน การใช้แผนที่คงเป็นวิธีการที่แนะนำโดยสถาปนิก Java
hfontanez

ฟิลด์ legIndex เกิดขึ้นพร้อมกับลำดับในตัวอย่างนี้ แต่สามารถเป็นค่า int ใดก็ได้ ไม่มีการค้นหาลำดับ นอกจากนี้โปรดระบุหรือเชื่อมโยงเหตุผลที่คุณคิดว่าการค้นหาลำดับไม่ถูกต้อง
Mike Adler

1
"ไม่พบขาด้วน?"
Gnagy

17

คำตอบของ adarshrปรับให้เข้ากับ Java 8:

import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toMap;

import java.util.Map;

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    private final static Map<Integer, LegNo> map =
            stream(LegNo.values()).collect(toMap(leg -> leg.legNo, leg -> leg));

    private LegNo(final int leg) {
        legNo = leg;
    }

    public static LegNo valueOf(int legNo) {
        return map.get(legNo);
    }
}

11

คุณยังสามารถเข้าถึงค่า Enum ที่สอดคล้องกับค่าจำนวนเต็มที่กำหนดได้ง่ายๆโดยการเรียกใช้วิธีการ values ​​() บน enum LegNo มันส่งคืนฟิลด์ของ LegNo enums: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

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


6

วิธี Java 8 พร้อมค่าเริ่มต้น:

public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private final int legNo;

    LegNo(int legNo) {
        this.legNo = legNo;
    }

    public static LegNo find(int legNo, Supplier<? extends LegNo> byDef) {
        return Arrays.asList(LegNo.values()).stream()
                .filter(e -> e.legNo == legNo).findFirst().orElseGet(byDef);
    }
}

โทร:

LegNo res = LegNo.find(0, () -> LegNo.NO_LEG);

หรือมีข้อยกเว้น:

LegNo res = LegNo.find(0, () -> {
    throw new RuntimeException("No found");
});

2
public enum LegNo {

  NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

  private final int code;

  LegNo(int code) {
    this.code = code;
    ReverseStorage.reverseMap.put(code, this);
  }

  public static Optional<LegNo> getByCode(int code) {
    return Optional.ofNullable(ReverseStorage.reverseMap.get(code));
  }

  private static final class ReverseStorage {
    private static final Map<Integer, LegNo> reverseMap = new LinkedHashMap<>();
  }
}

1

เนื่องจาก enum ของคุณมีเพียง 3 องค์ประกอบวิธีที่เร็วที่สุดคือใช้ชุดของif elseเช่นที่คุณแนะนำ

แก้ไข: คำตอบที่ adarshr ให้ไว้นั้นเหมาะกว่าสำหรับกรณีทั่วไปซึ่งมีค่า enum มากมาย แต่ฉันคิดว่ามันเกินความจำเป็นสำหรับปัญหาของคุณ


การมีMapรหัสของคุณไม่ได้เป็นการใช้งานมากเกินไป นอกจากนี้ยังทำให้วิธีนี้สะอาดกว่าสปาเก็ตตี้ของเงื่อนไข if-else
adarshr

ฉันยอมรับว่าแผนที่จะดีกว่าเมื่อคุณมีค่า enum จำนวนมาก แต่สำหรับ 3 ค่าฉันจะยึดติดกับโครงสร้าง if / else มันเป็นเรื่องของรสนิยมฉันเดา
DieterDP

ไม่ว่าเราจะเลือกวิธีใดก็ตามไม่public LegNo valueOf(int value)ควรเปลี่ยนลายเซ็นของวิธีการ if-else สามารถเขียนได้ภายใน enum เอง ถ้า if-else ออกมาจาก enum แน่นอนว่ามันจะกลายเป็นรหัสที่ไม่สะอาด
adarshr

1
ฉันเห็นด้วยกับคุณอย่างสมบูรณ์ :)
DieterDP

1
public enum LegNo {
    NO_LEG(-1), LEG_ONE(1), LEG_TWO(2);

    private int legNo;

    private LegNo(int leg) { legNo = leg; }

    public static LegNo valueOf(int legNo) {
        for (LegNo leg : LegNo.values()) {
            if (leg.legNo == legNo) return leg;
        }   
    }
}

assert LegNo.valueOf(2) == LegNo.LEG_TWO
assert LegNo.valueOf(3) == null

4
ยอมรับได้สำหรับ enum ที่มีค่า <10 แต่ไม่มีผลโดยสิ้นเชิงสำหรับค่า enum จำนวนมากเนื่องจากความซับซ้อนในการค้นหา O (n)
Alfishe
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.