Câu hỏi Tra cứu Java enum theo giá trị chuỗi


Nói rằng tôi có một enum mà chỉ là

public enum Blah {
    A, B, C, D
}

và tôi muốn tìm giá trị enum của một chuỗi, ví dụ "A" đó sẽ là Blah.A. Làm thế nào nó có thể làm được điều này?

Enum.valueOf() phương pháp tôi cần? Nếu vậy, tôi sẽ sử dụng cái này như thế nào?


1593
2018-03-02 22:56


gốc




Các câu trả lời:


Vâng, Blah.valueOf("A") sẽ cho bạn Blah.A.

Lưu ý rằng tên phải là chính xác phù hợp, bao gồm cả trường hợp: Blah.valueOf("a") và Blah.valueOf("A ") cả hai ném một IllegalArgumentException.

Các phương pháp tĩnh valueOf() và values() được tạo tại thời gian biên dịch và không xuất hiện trong mã nguồn. Tuy nhiên, chúng xuất hiện ở Javadoc; ví dụ, Dialog.ModalityType cho thấy cả hai phương pháp.


1858
2018-03-02 22:57



Để tham khảo, Blah.valueOf("A") phương pháp là trường hợp nhạy cảm và không chịu đựng khoảng trống không liên quan, do đó giải pháp thay thế được đề xuất dưới đây bởi @ JoséMi. - Brett
@Michael Myers, Vì câu trả lời này được bình chọn nhiều nhất từ ​​trước đến nay, tôi có nên hiểu rằng thực hành tốt để xác định một enum và giá trị String của nó giống hệt nhau không? - Kevin Meredith
@ KevinMeredith: Nếu bạn muốn nói toString() giá trị, không, tôi sẽ không nói điều đó. name() sẽ giúp bạn xác định tên thực tế của hằng số enum trừ khi bạn ghi đè lên nó. - Michael Myers♦
Ý của bạn chính xác là gì "được tạo tại thời gian biên dịch và không xuất hiện trong mã nguồn." ? - treesAreEverywhere
@treesAreEverywhere Cụ thể hơn, các phương pháp đó là được tạo(hoặc là tổng hợp) bởi trình biên dịch. Thực tế enum Blah {...} định nghĩa không nên cố gắng tự khai báo values cũng không valuesOf. Nó giống như cách bạn có thể viết "AnyTypeName.class" mặc dù bạn chưa bao giờ thực sự khai báo biến thành viên "class"; trình biên dịch làm cho nó tất cả chỉ cần làm việc. (Câu trả lời này có thể không còn hữu ích cho bạn 3 tháng sau đó, nhưng chỉ trong trường hợp.) - Ti Strga


Một giải pháp khác nếu văn bản không giống với giá trị điều tra:

public enum Blah {
  A("text1"),
  B("text2"),
  C("text3"),
  D("text4");

  private String text;

  Blah(String text) {
    this.text = text;
  }

  public String getText() {
    return this.text;
  }

  public static Blah fromString(String text) {
    for (Blah b : Blah.values()) {
      if (b.text.equalsIgnoreCase(text)) {
        return b;
      }
    }
    return null;
  }
}

711
2018-06-03 10:57



throw new IllegalArgumentException("No constant with text " + text + " found") sẽ tốt hơn return null. - whiskeysierra
@whiskeysierra Jon Skeet sẽ không đồng ý với điều đó. stackoverflow.com/questions/1167982/… - Sanghyun Lee
@Sangdol có thể bạn enlight chúng tôi tại sao trở về null là tốt hơn? - whiskeysierra
@ Sangdol thường là một điều tốt để kiểm tra những gì SUN - oops - Oracle đang làm trong tình huống tương tự. Và như Enum.valueOf () đang hiển thị nó LÀ thực hành tốt nhất để ném ngoại lệ trong trường hợp này. Bởi vì đó là một tình huống đặc biệt. "Tối ưu hóa hiệu suất" là một lý do xấu để viết mã không đọc được ;-) - raudi
Vâng, bạn cũng có thể sử dụng chú thích @Nullable để làm cho nó "dễ đọc" ;-) - JoséMi


Đây là một tiện ích tiện lợi mà tôi sử dụng:

/**
 * A common method for all enums since they can't have another base class
 * @param <T> Enum type
 * @param c enum type. All enums must be all caps.
 * @param string case insensitive
 * @return corresponding enum, or null
 */
public static <T extends Enum<T>> T getEnumFromString(Class<T> c, String string) {
    if( c != null && string != null ) {
        try {
            return Enum.valueOf(c, string.trim().toUpperCase());
        } catch(IllegalArgumentException ex) {
        }
    }
    return null;
}

Sau đó, trong lớp enum của tôi, tôi thường có điều này để tiết kiệm một số gõ:

public static MyEnum fromString(String name) {
    return getEnumFromString(MyEnum.class, name);
}

Nếu enums của bạn không phải là tất cả các mũ, chỉ cần thay đổi Enum.valueOf hàng.

Quá tệ tôi không thể sử dụng T.class cho Enum.valueOf như T bị xóa.


98
2017-11-12 15:52



Đó là khối catch rỗng thực sự khiến tôi cảm thấy rất tiếc, xin lỗi. - whiskeysierra
@LazloBonin: Các ngoại lệ dành cho các điều kiện đặc biệt, không dành cho luồng điều khiển. Nhận bản thân Java hiệu quả. - Martin Schröder
Nếu Java API bạn muốn sử dụng ném một ngoại lệ và bạn không muốn mã của bạn để ném một, bạn có thể nuốt các trường hợp ngoại lệ như thế này, hoặc viết lại logic từ đầu nên không có ngoại lệ được ném ở nơi đầu tiên. Nuốt ngoại lệ thường là cái ác ít hơn. - Nate C-K
Kinh khủng! Luôn luôn, luôn luôn bắt ngoại lệ nơi bạn có thể xử lý chúng. Ví dụ trên là một ví dụ hoàn hảo làm thế nào để không làm điều đó. Tại sao? Vì vậy, nó trả về NULL, và người gọi sau đó phải kiểm tra NULL hoặc ném một NPE. Nếu người gọi biết cách xử lý tình huống thì hãy làm nếu so với try-catch có thể trông thanh lịch hơn một chút, NHƯNG nếu anh ta không thể xử lý anh ta phải vượt qua null một lần nữa và người gọi của người gọi lần nữa phải kiểm tra NULL, v.v. - raudi
Để công bằng với giải pháp trên, thực sự có các trường hợp yêu cầu bạn trả về null thay vì ném IllegalArgumentException và phá vỡ luồng chương trình của bạn, ví dụ, ánh xạ các enums giữa lược đồ dịch vụ web và lược đồ cơ sở dữ liệu trong đó chúng không phải luôn luôn -đến một. Tuy nhiên, tôi đồng ý rằng khối catch không bao giờ được để trống. Đặt một số mã như log.warn hoặc một cái gì đó cho mục đích theo dõi. - Adrian M


Bạn cũng nên cẩn thận với trường hợp của bạn. Hãy để tôi giải thích: làm Blah.valueOf("A") hoạt động, nhưng Blah.valueOf("a") sẽ không làm việc. Sau đó một lần nữa Blah.valueOf("a".toUpperCase(Locale.ENGLISH)) sẽ hoạt động.

chỉnh sửa
Đã thay đổi toUpperCase đến toUpperCase(Locale.ENGLISH) dựa trên tc. bình luận và tài liệu java

edit2 Trên Android, bạn nên sử dụng Locale.US, như sulai chỉ ra.


66
2018-05-09 16:33



Hãy cảnh giác với ngôn ngữ mặc định! - tc.
Đối với người dùng Android, tôi muốn chỉ ra rằng Tài liệu Android rõ ràng khuyến khích việc sử dụng Locale.US cho đầu vào / đầu ra có thể đọc được máy. - sulai
Là trường hợp trên khác nhau ở những nơi khác nhau? - Holloway
@Trengot Vâng. - João Portela
@Trengot Vâng, thật không may. Thổ Nhĩ Kỳ là một ví dụ tốt. Kết hợp điều này với việc xử lý các bộ ký tự mặc định bị hỏng của Java (mặc định là tiếng Latin trên Windows thay vì Unicode) và bạn sẽ thấy hầu như không an toàn khi sử dụng các phiên bản mặc định của các phương thức chấp nhận bộ ký tự hoặc ngôn ngữ. Bạn hầu như luôn phải xác định rõ ràng chúng. - Stijn de Witt


Sử dụng mẫu từ Joshua Bloch, Java hiệu quả:

(đơn giản hóa cho ngắn gọn)

enum MyEnum {
  ENUM_1("A"),
  ENUM_2("B");

  private String name;

  private static final Map<String,MyEnum> ENUM_MAP;

  MyEnum (String name) {
    this.name = name;
  }

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

  // Build an immutable map of String name to enum pairs.
  // Any Map impl can be used.

  static {
    Map<String,MyEnum> map = new ConcurrentHashMap<String,MyEnum>();
    for (MyEnum instance : MyEnum.values()) {
      map.put(instance.getName(),instance);
    }
    ENUM_MAP = Collections.unmodifiableMap(map);
  }

  public static MyEnum get (String name) {
    return ENUM_MAP.get(name);
  }
}

Cũng thấy:

Ví dụ Java Java bằng Enum và Bản đồ các cá thể

Thứ tự thực hiện của các khối tĩnh trong một kiểu Enum

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


38
2018-06-15 16:37



nếu Joshua Bloch nói thì đây là cách duy nhất để đi :-). Đó là một sự xấu hổ mà tôi luôn luôn phải di chuyển xuống đây. - dermoritz
Điều này thậm chí còn đơn giản hơn trong Java 8 như bạn có thể làm:Stream.of(MyEnum.values()).collect(toMap(Enum::name, identity())) Tôi cũng khuyên bạn nên ghi đè toString () (truyền vào thông qua hàm tạo) và sử dụng thay vì tên, đặc biệt nếu Enum được kết hợp với dữ liệu tuần tự vì điều này cho phép bạn kiểm soát vỏ mà không cho Sonar phù hợp. - Novaterata
Java 8 chắc chắn có thể / sẽ thay đổi rất nhiều câu trả lời (tốt hơn) trên diễn đàn này. Không chắc chắn về việc bao giờ có đuôi (Sonar) wag con chó (mã ứng dụng) mặc dù. - Darrell Teague
Nếu bạn định đặt nó vào unmodifiableMap dù sao thì không có lợi khi bắt đầu bằng ConcurrentHashMap. Chỉ cần sử dụng một HashMap. (Nếu bạn có Guava's ImmutableMap sau đó tôi khuyên bạn nên thay thế!) - Daniel Pryden
Bản đồ ImmutableMap của Google chắc chắn là một thực thi tốt hơn (thực sự không thay đổi) nhưng với mục đích thể hiện giải pháp Java lõi đơn ... và tập trung vào trường hợp tra cứu bản đồ chuỗi, chỉ các lớp Java lõi được sử dụng. Nói chung, đã thấy rằng ConcurrentHashMap hầu như luôn được ưu tiên hơn HashMap cho toàn bộ hồ sơ hiệu suất (thêm, loại bỏ, sửa đổi) và đơn giản so với gói / khóa HashMap khi sửa đổi đồng thời phải được xem xét. - Darrell Teague


Đây là một phương pháp có thể làm điều đó cho bất kỳ Enum nào, và không phân biệt chữ hoa chữ thường.

/** 
 * Finds the value of the given enumeration by name, case-insensitive. 
 * Throws an IllegalArgumentException if no match is found.  
 **/
public static <T extends Enum<T>> T valueOfIgnoreCase(
        Class<T> enumeration, String name) {

    for (T enumValue : enumeration.getEnumConstants()) {
        if (enumValue.name().equalsIgnoreCase(name)) {
            return enumValue;
        }
    }

    throw new IllegalArgumentException(String.format(
        "There is no value with name '%s' in Enum %s",
        name, enumeration.getName()
    ));
}

34
2017-12-01 21:53



Biến thể này đang thực hiện chính xác: equalsIgnoreCase là con đường để đi. +1 - Stijn de Witt


Sử dụng Blah.valueOf(string) là tốt nhất nhưng bạn có thể sử dụng Enum.valueOf(Blah.class, string) cũng.


28
2018-05-09 17:23



Trường hợp nhạy cảm, không giúp đỡ! - Murtaza Kanchwala
@MurtazaKanchwala Bạn có thể làm rõ nhận xét của mình không? Bạn đang cố làm gì vậy? - Peter Lawrey
Xin chào @PeterLawrey, tôi đã cố gắng tìm nạp một Enum từ một String public enum ObjectType {PERSON ("Person") public String parameterName; ObjectType (String parameterName) {this.parameterName = parameterName; } public String getParameterName () {return this.parameterName; } public static ObjectType fromString (String parameterName) {if (parameterName! = null) {for (ObjectType objType: ObjectType.values ​​()) {if (parameterName.equalsIgnoreCase (objType.parameterName)) {return objType; }}} trả về null; }} - Murtaza Kanchwala


Nếu bạn không muốn viết tiện ích của riêng mình, hãy sử dụng Google  thư viện:

Enums.getIfPresent(Blah.class, "A")

Không giống như hàm java được xây dựng, bạn hãy kiểm tra xem A có hiện diện trong Blah và không ném một ngoại lệ hay không.


22
2018-04-02 20:12



phần buồn là, điều này trả về một google Tùy chọn và không java Tùy chọn - javaProgrammer


Bạn có thể cần đến điều này:

public enum ObjectType {
    PERSON("Person");

    public String parameterName;

    ObjectType(String parameterName) {
        this.parameterName = parameterName;
    }

    public String getParameterName() {
        return this.parameterName;
    }

    //From String method will return you the Enum for the provided input string
    public static ObjectType fromString(String parameterName) {
        if (parameterName != null) {
            for (ObjectType objType : ObjectType.values()) {
                if (parameterName.equalsIgnoreCase(objType.parameterName)) {
                    return objType;
                }
            }
        }
        return null;
    }
}

Thêm một bổ sung:

   public static String fromEnumName(String parameterName) {
        if (parameterName != null) {
            for (DQJ objType : DQJ.values()) {
                if (parameterName.equalsIgnoreCase(objType.name())) {
                    return objType.parameterName;
                }
            }
        }
        return null;
    }

Điều này sẽ trả lại cho bạn Giá trị bằng Tên đại diện được xâu chuỗi Ví dụ: nếu bạn cung cấp "PERSON" trong fromEnumName, nó sẽ trả lại cho bạn Giá trị của Enum tức là "Người"


13
2017-07-02 11:54





Trong Java 8 hoặc mới hơn, sử dụng Dòng:

public enum Blah
{
    A("text1"),
    B("text2"),
    C("text3"),
    D("text4");

    private String text;

    Blah(String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }

    public static Blah fromText(String text) {
        return Arrays.stream(values())
          .filter(bl -> bl.text.equalsIgnoreCase(text))
          .findFirst()
          .orElse(null);
    }
}

11
2017-08-01 10:17