Bắt enum liên kết với giá trị int


88

Trước đây, tôi có các enums LegNo của mình được định nghĩa đơn giản là:

NO_LEG, LEG_ONE, LEG_TWO

và bằng cách gọi return LegNo.values()[i];, tôi đã có thể nhận được giá trị được liên kết với mỗi enum.

Nhưng bây giờ tôi đã quyết định tôi muốn LegNoenum NO_LEGlà int -1 thay vì 0 vì vậy tôi quyết định sử dụng một hàm tạo riêng để khởi tạo và đặt giá trị int của nó

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

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

điều duy nhất bây giờ là vì tôi đang làm theo cách này nên values()phương thức sẽ không hoạt động đối với NO_LEGenum. Làm cách nào để lấy enum được liên kết với int? Có cách nào hiệu quả để thực hiện việc này ngoài việc sử dụng câu lệnh chuyển đổi trường hợp hoặc if-elseif-elseif không

Tôi có thể thấy rất nhiều câu hỏi SO để làm với việc lấy giá trị int từ enum, nhưng tôi thì ngược lại.

Câu trả lời:


146

CHỈNH SỬA tháng 8 năm 2018

Hôm nay tôi sẽ thực hiện điều này như sau

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

Bạn sẽ phải duy trì một ánh xạ bên trong 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);
    }
}

Khối tĩnh sẽ chỉ được gọi một lần, vì vậy thực tế không có vấn đề về hiệu suất ở đây.

CHỈNH SỬA: Đã đổi tên phương thức thành valueOfvì nó nội dòng hơn với các lớp Java khác.


xin lỗi, tôi không chắc liệu tôi có đủ rõ ràng hay không. tôi muốn chuyển int và lấy enum liên kết với nó.
L-Samuels

@ L-Samuels Đoán tôi đã đọc câu hỏi của bạn không đúng cách. Xem bản cập nhật của tôi.
adarshr

2
Tôi biết điều này có vẻ hiển nhiên, nhưng sử dụng nó như vậy: LegNo foo = LegNo.valueOf(2);. Mã trước đó sẽ trả về a LegNo.LEG_TWO.
FirstOne

1
Cần lưu ý, việc chuyển một giá trị số nguyên không hợp lệ (không được ánh xạ) sẽ trả về null, như mong đợi bằng cách sử dụng HashMap.get : Trả về giá trị mà khóa đã chỉ định được ánh xạ hoặc null nếu bản đồ này không chứa ánh xạ cho khóa.
FirstOne

Mặc dù cú pháp của luồng là gọn gàng, nhưng điều đáng nói là nó có độ phức tạp về thời gian cao hơn so với bản đồ tĩnh (phải thừa nhận là có mức tiêu thụ bộ nhớ cao hơn). Không phải là vấn đề đối với 3 giá trị, nhưng chắc chắn là một mối quan tâm nếu bạn đang nhập valueOf()một enum 1000 thành viên bên trong một vòng lặp khác.
Patrick M

24

Bạn cũng có thể bao gồm một phương thức tĩnh trong enum để lặp qua tất cả các thành viên và trả về đúng.

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

Bây giờ, nếu bạn muốn nhận giá trị Enum bằng số nguyên, bạn chỉ cần sử dụng:

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

Tôi cho rằng điều này sẽ thanh lịch hơn so với việc sử dụng câu lệnh if else if. Nhưng nếu có nhiều tìm kiếm trên enumsto thì chiến lược bản đồ do @adarshr đề xuất sẽ tốt hơn. Mặc dù bình chọn cho sự hài hước.
L-Samuels

1
Tôi cũng rất thích chiến lược bản đồ. Đặc biệt là khi enum có rất nhiều giá trị hoặc nó phải được tra cứu thông qua cơ chế này rất thường xuyên. Tuy nhiên, nếu việc tìm kiếm các giá trị bằng int được liên kết là một điều tương đối hiếm hoặc bạn có nhiều enum khác nhau với cùng một yêu cầu tra cứu, tôi tin rằng cách của tôi sẽ thân thiện với tài nguyên hơn, vì chi phí cho bản đồ được lưu. Thêm vào đó, tôi thấy nó làm cho mã ít lộn xộn hơn. Tuy nhiên, tôi có một số trường hợp sử dụng mà tôi chắc chắn sẽ tự mình chuyển sang Loại bản đồ.
Mike Adler

Bạn không bao giờ nên lấy một giá trị enum liên quan theo thứ tự của nó. Sử dụng bản đồ tĩnh LÀ phương pháp được các kiến ​​trúc sư Java đề xuất.
hfontanez

Trường legIndex trùng với thứ tự trong ví dụ này, nhưng có thể là bất kỳ giá trị int nào. Không có tra cứu thứ tự được thực hiện. Ngoài ra, vui lòng cung cấp hoặc liên kết lý do tại sao bạn cho rằng tra cứu theo thứ tự là không tốt.
Mike Adler

1
"Không tìm thấy chân. Bị cắt cụt?"
Gnagy

17

câu trả lời của adarshr thích ứng với 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

Bạn cũng có thể truy cập giá trị Enum tương ứng với giá trị số nguyên đã cho đơn giản bằng cách gọi phương thức giá trị () trên enum LegNo. Nó trả về trường LegNo enums: LegNo.values()[0]; //returns LEG_NO LegNo.values()[1]; //returns LEG_ONE LegNo.values()[2]; //returns LEG_TWO

Không chính xác là thứ anh ta đang tìm kiếm, nhưng khá gần và thực sự rất đơn giản. (Mặc dù đối tượng đã chết nhưng nó có thể hữu ích cho người khác.)


6

Java 8 cách với giá trị mặc định:

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

để gọi:

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

hoặc với Ngoại lệ:

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

1

Vì enum của bạn chỉ chứa 3 phần tử, nên cách nhanh nhất sẽ là chỉ sử dụng một loạt các phần tử if else, như bạn đã đề xuất.

chỉnh sửa: câu trả lời mà adarshr cung cấp phù hợp hơn cho các trường hợp chung, nơi có nhiều giá trị enum, nhưng tôi nghĩ rằng đó là một sự quá mức cần thiết cho vấn đề của bạn.


Có một Mapmã trong mã của bạn chắc chắn không phải là một quá mức cần thiết. Bên cạnh đó, nó làm cho phương pháp sạch hơn rất nhiều so với một món mì Ý trong điều kiện if-else.
adarshr

Tôi đồng ý rằng Bản đồ sẽ tốt hơn khi bạn có nhiều giá trị enum, nhưng đối với 3 giá trị, tôi sẽ gắn bó với cấu trúc if / else. Tôi đoán đó là vấn đề về hương vị.
DieterDP

Dù chúng ta chọn cách tiếp cận nào, thì chữ ký của phương thức public LegNo valueOf(int value)không được thay đổi. Sau đó if-else có thể được viết trong chính enum. Nếu if-else thoát ra khỏi enum, thì nó chắc chắn sẽ trở thành một mã không sạch.
adarshr

1
Tôi đồng ý với bạn hoàn toàn :)
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
Có thể chấp nhận cho enum có giá trị <10 nhưng hoàn toàn không hiệu quả đối với số lượng lớn giá trị enum vì độ phức tạp tra cứu O (n)
Alfishe

1
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<>();
  }
}
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.