Làm cách nào tôi có thể tra cứu một enum Java từ giá trị Chuỗi của nó?


164

Tôi muốn tra cứu một enum từ giá trị chuỗi của nó (hoặc có thể là bất kỳ giá trị nào khác). Tôi đã thử đoạn mã sau nhưng nó không cho phép khởi tạo tĩnh. Có một cách đơn giản?

public enum Verbosity {

    BRIEF, NORMAL, FULL;

    private static Map<String, Verbosity> stringMap = new HashMap<String, Verbosity>();

    private Verbosity() {
        stringMap.put(this.toString(), this);
    }

    public static Verbosity getVerbosity(String key) {
        return stringMap.get(key);
    }
};

IIRC, điều đó mang lại một NPE vì việc khởi tạo tĩnh được thực hiện từ trên xuống (tức là các hằng số enum ở trên cùng được xây dựng trước khi nó bắt stringMapđầu khởi tạo). Giải pháp thông thường là sử dụng một lớp lồng nhau.
Tom Hawtin - tackline

Cảm ơn tất cả mọi người vì phản ứng nhanh chóng như vậy. (FWIW Tôi không thấy Sun Javadocs rất hữu ích cho vấn đề này).
peter.m bồ.rust

Đó thực sự là một vấn đề ngôn ngữ hơn là một vấn đề thư viện. Tuy nhiên, tôi nghĩ rằng các tài liệu API được đọc nhiều hơn JLS (mặc dù có lẽ không phải bởi các nhà thiết kế ngôn ngữ), vì vậy những thứ như thế này có lẽ sẽ có nhiều điểm nổi bật hơn trong các tài liệu java.lang.
Tom Hawtin - tackline

Câu trả lời:


241

Sử dụng valueOfphương thức được tạo tự động cho mỗi Enum.

Verbosity.valueOf("BRIEF") == Verbosity.BRIEF

Đối với các giá trị tùy ý bắt đầu bằng:

public static Verbosity findByAbbr(String abbr){
    for(Verbosity v : values()){
        if( v.abbr().equals(abbr)){
            return v;
        }
    }
    return null;
}

Chỉ chuyển sang sau để thực hiện Bản đồ nếu trình hồ sơ của bạn cho bạn biết.

Tôi biết rằng nó lặp đi lặp lại trên tất cả các giá trị, nhưng chỉ với 3 giá trị enum, nó hầu như không xứng đáng với bất kỳ nỗ lực nào khác, thực tế trừ khi bạn có nhiều giá trị tôi sẽ không bận tâm với Bản đồ, nó sẽ đủ nhanh.


cảm ơn - và tôi có thể sử dụng chuyển đổi trường hợp nếu tôi biết các giá trị vẫn khác biệt
peter.m bồ.rust

Bạn có thể truy cập trực tiếp thành viên lớp 'abbr' thay vì "v.abbr ()", bạn có thể sử dụng "v.abbr.equals ...".
Amio.io

7
Ngoài ra, đối với các giá trị tùy ý (trường hợp thứ hai), hãy xem xét việc ném IllegalArgumentExceptionthay vì trả về null ( nghĩa là khi không tìm thấy kết quả khớp nào) - dù sao đây cũng là cách mà JDK Enum.valueOf(..)thực hiện.
Priidu Neemre

135

Bạn đang ở gần. Đối với các giá trị tùy ý, hãy thử một cái gì đó như sau:

public enum Day { 

    MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"),
    THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ;

    private final String abbreviation;

    // Reverse-lookup map for getting a day from an abbreviation
    private static final Map<String, Day> lookup = new HashMap<String, Day>();

    static {
        for (Day d : Day.values()) {
            lookup.put(d.getAbbreviation(), d);
        }
    }

    private Day(String abbreviation) {
        this.abbreviation = abbreviation;
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public static Day get(String abbreviation) {
        return lookup.get(abbreviation);
    }
}

4
Do các vấn đề của trình nạp lớp, phương pháp này sẽ không hoạt động đáng tin cậy. Tôi không khuyên bạn nên và tôi đã thấy nó thất bại thường xuyên vì bản đồ tra cứu chưa sẵn sàng trước khi được truy cập. Bạn cần đặt "tra cứu" bên ngoài enum hoặc trong một lớp khác để nó sẽ tải trước.
Adam Gent

7
@Adam Gent: Có một liên kết bên dưới tới JLS nơi cấu trúc chính xác này được đặt làm ví dụ. Nó nói rằng khởi tạo tĩnh xảy ra từ trên xuống dưới: docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#d5e12267
Selena

3
Có java hiện đại như java 8 đã sửa mô hình bộ nhớ. Điều đó đang được nói rằng các tác nhân hoán đổi nóng như jrebel vẫn bị rối nếu bạn nhúng việc khởi tạo vì các tham chiếu vòng tròn.
Adam Gent

@Adam Gent: Hừm. Tôi học được điều gì đó mới mỗi ngày. [Lặng lẽ rời đi để cấu trúc lại enum của tôi với các singletons bootstrap ...] Xin lỗi nếu đây là một câu hỏi ngu ngốc, nhưng đây có phải là vấn đề với khởi tạo tĩnh không?
Selena

Tôi chưa bao giờ thấy các vấn đề với khối mã tĩnh không được khởi tạo, nhưng tôi chỉ sử dụng Java 8 kể từ khi tôi bắt đầu sử dụng Java, vào năm 2015. Tôi chỉ thực hiện một điểm chuẩn nhỏ bằng cách sử dụng JMH 1.22 và sử dụng một HashMap<>enum để chứng minh là nhanh hơn 40% so với vòng lặp foor.
cbaldan

21

với Java 8 bạn có thể đạt được theo cách này:

public static Verbosity findByAbbr(final String abbr){
    return Arrays.stream(values()).filter(value -> value.abbr().equals(abbr)).findFirst().orElse(null);
}

Tôi sẽ ném một ngoại lệ hơn là trở về null. Nhưng nó cũng phụ thuộc vào trường hợp sử dụng
alexander

12

Câu trả lời của @ Lyle khá nguy hiểm và tôi đã thấy nó không hoạt động đặc biệt nếu bạn biến enum thành một lớp bên trong tĩnh. Thay vào đó, tôi đã sử dụng một cái gì đó như thế này để tải các bản đồ BootstrapSingleton trước enum.

Chỉnh sửa điều này không còn là vấn đề nữa với các JVM hiện đại (JVM 1.6 trở lên) nhưng tôi nghĩ vẫn còn vấn đề với JRebel nhưng tôi chưa có cơ hội kiểm tra lại .

Tải cho tôi trước:

   public final class BootstrapSingleton {

        // Reverse-lookup map for getting a day from an abbreviation
        public static final Map<String, Day> lookup = new HashMap<String, Day>();
   }

Bây giờ tải nó trong hàm tạo enum:

   public enum Day { 
        MONDAY("M"), TUESDAY("T"), WEDNESDAY("W"),
        THURSDAY("R"), FRIDAY("F"), SATURDAY("Sa"), SUNDAY("Su"), ;

        private final String abbreviation;

        private Day(String abbreviation) {
            this.abbreviation = abbreviation;
            BootstrapSingleton.lookup.put(abbreviation, this);
        }

        public String getAbbreviation() {
            return abbreviation;
        }

        public static Day get(String abbreviation) {
            return lookup.get(abbreviation);
        }
    }

Nếu bạn có một enum bên trong, bạn chỉ cần xác định Map bên trên định nghĩa enum và rằng (về lý thuyết) sẽ được tải trước đó.


3
Cần định nghĩa "nguy hiểm" và tại sao việc thực hiện lớp bên trong lại quan trọng. Đây là vấn đề định nghĩa tĩnh từ trên xuống dưới nhưng có mã làm việc trong một câu trả lời liên quan khác có liên kết quay lại câu hỏi này sử dụng Bản đồ tĩnh trên chính đối tượng Enum với phương thức "get" từ giá trị do người dùng xác định Enum loại. stackoverflow.com/questions/604424/lookup-enum-by-opes-value
Darrell Teague

2
@DarrellTeague vấn đề ban đầu là do các JVM cũ (trước 1.6) hoặc các JVM khác (IBM) hoặc các trình trao đổi mã nóng (JRebel). Vấn đề không nên xảy ra trong các JVM hiện đại vì vậy tôi có thể xóa câu trả lời của mình.
Adam Gent

Tốt để lưu ý rằng thực sự có thể do triển khai trình biên dịch tùy chỉnh và tối ưu hóa thời gian chạy ... việc đặt hàng có thể được kiểm tra lại, dẫn đến lỗi. Tuy nhiên, điều này sẽ xuất hiện để vi phạm thông số kỹ thuật.
Darrell Teague

7

Và bạn không thể sử dụng valueOf () ?

Chỉnh sửa: Btw, không có gì ngăn bạn sử dụng tĩnh {} trong enum.


Hoặc tổng hợp valueOftrên lớp enum, vì vậy bạn không cần chỉ định lớp enum Class.
Tom Hawtin - tackline

Tất nhiên, nó chỉ không có mục javadoc nên rất khó để liên kết.
Fredrik

1
Bạn có thể sử dụng valueOf () nhưng tên phải khớp chính xác với tên được đặt trong khai báo enum. Một trong hai phương pháp tra cứu ở trên có thể được sửa đổi để sử dụng .equalsIgnoringCase () và có một chút mạnh mẽ hơn để sửa lỗi.

@leonardo Đúng. Nếu nó thêm mạnh mẽ hoặc chỉ chịu lỗi là tranh cãi. Trong hầu hết các trường hợp, tôi sẽ nói đó là cái sau và trong trường hợp đó tốt hơn là xử lý nó ở nơi khác và vẫn sử dụng valueOf () của Enum.
Fredrik

4

Trong trường hợp nó giúp người khác, tùy chọn tôi thích, không được liệt kê ở đây, sử dụng chức năng Bản đồ của Guava :

public enum Vebosity {
    BRIEF("BRIEF"),
    NORMAL("NORMAL"),
    FULL("FULL");

    private String value;
    private Verbosity(final String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }

    private static ImmutableMap<String, Verbosity> reverseLookup = 
            Maps.uniqueIndex(Arrays.asList(Verbosity.values()), Verbosity::getValue);

    public static Verbosity fromString(final String id) {
        return reverseLookup.getOrDefault(id, NORMAL);
    }
}

Với mặc định bạn có thể sử dụng null, bạn có thể throw IllegalArgumentExceptionhoặc bạn fromStringcó thể trả lại Optionalbất kỳ hành vi nào bạn thích.


3

kể từ java 8, bạn có thể khởi tạo bản đồ trong một dòng duy nhất và không có khối tĩnh

private static Map<String, Verbosity> stringMap = Arrays.stream(values())
                 .collect(Collectors.toMap(Enum::toString, Function.identity()));

+1 Sử dụng tuyệt vời các luồng và rất súc tích mà không làm mất khả năng đọc! Tôi cũng chưa thấy sử dụng này Function.identity()trước đây, tôi thấy nó hấp dẫn hơn về mặt văn bản hơn e -> e.
Matsu Q.

0

Có lẽ, hãy xem điều này. Nó làm việc cho tôi. Mục đích của việc này là tìm kiếm 'ĐỎ' với '/ red_color'. Khai báo a static mapvà tải enums vào nó chỉ một lần sẽ mang lại một số lợi ích hiệu suất nếu số enumlượng nhiều.

public class Mapper {

public enum Maps {

    COLOR_RED("/red_color", "RED");

    private final String code;
    private final String description;
    private static Map<String, String> mMap;

    private Maps(String code, String description) {
        this.code = code;
        this.description = description;
    }

    public String getCode() {
        return name();
    }

    public String getDescription() {
        return description;
    }

    public String getName() {
        return name();
    }

    public static String getColorName(String uri) {
        if (mMap == null) {
            initializeMapping();
        }
        if (mMap.containsKey(uri)) {
            return mMap.get(uri);
        }
        return null;
    }

    private static void initializeMapping() {
        mMap = new HashMap<String, String>();
        for (Maps s : Maps.values()) {
            mMap.put(s.code, s.description);
        }
    }
}
}

Xin vui lòng đặt trong ý của bạn.


-1

Bạn có thể định nghĩa Enum của mình theo mã sau:

public enum Verbosity 
{
   BRIEF, NORMAL, FULL, ACTION_NOT_VALID;
   private int value;

   public int getValue()
   {
     return this.value;
   } 

   public static final Verbosity getVerbosityByValue(int value)
   {
     for(Verbosity verbosity : Verbosity.values())
     {
        if(verbosity.getValue() == value)
            return verbosity ;
     }

     return ACTION_NOT_VALID;
   }

   @Override
   public String toString()
   {
      return ((Integer)this.getValue()).toString();
   }
};

Xem liên kết sau để biết thêm


-1

Nếu bạn muốn có một giá trị mặc định và không muốn xây dựng bản đồ tra cứu, bạn có thể tạo một phương thức tĩnh để xử lý điều đó. Ví dụ này cũng xử lý các tra cứu trong đó tên dự kiến ​​sẽ bắt đầu bằng một số.

    public static final Verbosity lookup(String name) {
        return lookup(name, null);
    }

    public static final Verbosity lookup(String name, Verbosity dflt) {
        if (StringUtils.isBlank(name)) {
            return dflt;
        }
        if (name.matches("^\\d.*")) {
            name = "_"+name;
        }
        try {
            return Verbosity.valueOf(name);
        } catch (IllegalArgumentException e) {
            return dflt;
        }
    }

Nếu bạn cần nó trên một giá trị thứ cấp, bạn sẽ chỉ cần xây dựng bản đồ tra cứu trước như trong một số câu trả lời khác.


-1
public enum EnumRole {

ROLE_ANONYMOUS_USER_ROLE ("anonymous user role"),
ROLE_INTERNAL ("internal role");

private String roleName;

public String getRoleName() {
    return roleName;
}

EnumRole(String roleName) {
    this.roleName = roleName;
}

public static final EnumRole getByValue(String value){
    return Arrays.stream(EnumRole.values()).filter(enumRole -> enumRole.roleName.equals(value)).findFirst().orElse(ROLE_ANONYMOUS_USER_ROLE);
}

public static void main(String[] args) {
    System.out.println(getByValue("internal role").roleName);
}

}


-2

Bạn có thể sử dụng Enum::valueOf()chức năng như được đề xuất bởi Gareth Davis & Brad Mace ở trên, nhưng hãy đảm bảo bạn xử lý IllegalArgumentExceptioncái sẽ bị ném nếu chuỗi được sử dụng không có trong enum.

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.