Java: Kiểm tra xem enum có chứa một chuỗi đã cho không?


169

Đây là vấn đề của tôi - Tôi đang tìm kiếm (nếu nó còn tồn tại) tương đương với enum ArrayList.contains();.

Đây là một ví dụ về vấn đề mã của tôi:

enum choices {a1, a2, b1, b2};

if(choices.???(a1)}{
//do this
} 

Bây giờ, tôi nhận ra rằng một ArrayListtrong số Stringsđó sẽ là con đường tốt hơn ở đây nhưng tôi phải chạy nội dung enum của mình thông qua một công tắc / trường hợp ở nơi khác. Do đó vấn đề của tôi.

Giả sử một cái gì đó như thế này không tồn tại, làm thế nào tôi có thể thực hiện nó?


Chuyển đổi / trường hợp với chuỗi được triển khai bắt đầu từ Java 7
AndreyP

Câu trả lời:


205

Điều này nên làm điều đó:

public static boolean contains(String test) {

    for (Choice c : Choice.values()) {
        if (c.name().equals(test)) {
            return true;
        }
    }

    return false;
}

Cách này có nghĩa là bạn không phải lo lắng về việc thêm các giá trị enum sau này, tất cả chúng đều được kiểm tra.

Chỉnh sửa: Nếu enum rất lớn, bạn có thể dán các giá trị trong Hashset:

public static HashSet<String> getEnums() {

  HashSet<String> values = new HashSet<String>();

  for (Choice c : Choice.values()) {
      values.add(c.name());
  }

  return values;
}

Sau đó, bạn có thể làm: values.contains("your string")trả về đúng hay sai.


12
đó là một hàm ý rất kém: Lựa chọn.valueOf (thử nghiệm) là những gì bạn muốn (w / thử / bắt)
bestsss

17
bestsss, đây rõ ràng là giải pháp thích hợp nhất. Ném một ngoại lệ để thực hiện một loại phương thức tồn tại () là thực tiễn xấu. Mặc dù bạn có thể nghĩ rằng việc triển khai của mình hiệu quả hơn do nó không tìm kiếm O (n), nhưng nó nằm trong khung cơ bản không nhìn thấy được. Đồng thời sử dụng thử {} bắt thêm chi phí. Thêm vào đó, nó không đẹp.
jluzwick

25
@Jared, chắc chắn valueOf. Chỉ cần bắt ngoại lệ của bạn và trả lại sai. Đối với những người nói khác, nếu bạn nhìn vào việc triển khai, nó đã sử dụng Bản đồ và các nhà phát triển JDK có cơ hội tốt hơn nhiều để tối ưu hóa điều này. API đưa ra một ngoại lệ, đó là một thực tiễn gây tranh cãi (thay vì trả về null), nhưng khi bạn xử lý một API ném ngoại lệ, hãy đi với nó, đừng phát minh lại bánh xe.
Yishai

2
@jluzwick thử / bắt trên không là một hướng dẫn nhảy đơn giản khi không được thực hiện, không sử dụng Ngoại lệ trong khối bắt cũng được tối ưu hóa. Sợ thử / bắt nguyên nhân mất hiệu suất là một thực tế xấu.
bestsss

22
chứa () sẽ được ưu tiên hơn valueOf () với một ngoại lệ. Tại sao? Bởi vì "ngoại lệ, như tên gọi của chúng, chỉ được sử dụng cho các điều kiện ngoại lệ; chúng không bao giờ được sử dụng cho luồng điều khiển thông thường" (Joshua Bloch, "Java hiệu quả").
james.garriss

226

Sử dụng Apache commons lang3 lib thay thế

 EnumUtils.isValidEnum(MyEnum.class, myValue)

33
Lưu ý đối với những người quan tâm, việc triển khai cơ bản mà họ đã sử dụng chỉ đơn giản là giải pháp thử / bắt (@since 3.0 @version $ Id: EnumUtils.java 1199894 2011-11-09 17: 53: 59Z ggregory $).
Jonathan Gawrych

1
Làm cho mã của tôi đơn giản hơn, vì vậy tôi không quan tâm nếu nó sử dụng ngoại lệ cho kiểm soát luồng (họ thực sự không nên) ... Thật tuyệt nếu họ thay đổi điều đó.
jpangamarca


1
Có phải ổi cũng chứa một giải pháp như thế này?
Cypress Frankenfeld

50

Bạn có thể dùng Enum.valueOf()

enum Choices{A1, A2, B1, B2};

public class MainClass {
  public static void main(String args[]) {
    Choices day;

    try {
       day = Choices.valueOf("A1");
       //yes
    } catch (IllegalArgumentException ex) {  
        //nope
  }
}

Nếu bạn mong đợi kiểm tra thất bại thường xuyên, bạn có thể tốt hơn bằng cách sử dụng một vòng lặp đơn giản như những gì khác đã hiển thị - nếu enum của bạn chứa nhiều giá trị, có thể là builda HashSethoặc tương tự các giá trị enum của bạn được chuyển đổi thành một chuỗi và HashSetthay vào đó truy vấn .


8
Tôi không nghĩ Exception là lựa chọn tốt nhất trong trường hợp như vậy.
GokcenG

6
Hãy thử và Catch nên là phương sách cuối cùng. Thử và bắt là đắt
Jesus Dimrix

2
dựa vào các ngoại lệ thời gian chạy để làm logic kinh doanh, bên cạnh việc đắt tiền, không thể đọc được. liên quan đến các ngoại lệ được kiểm tra, nó khác nhau, bởi vì những điều đó tạo nên một phần của doanh nghiệp.
Luís Soares

2
Điều này cũng ngăn không cho bất kỳ ai bật tắt các trường hợp ngoại lệ bị ném để tìm các trường hợp ngoại lệ thực tế đang được thử lại. (hoặc ít nhất làm cho nó rất khó chịu để làm như vậy). Sử dụng ngoại lệ cho các trường hợp đặc biệt.
Nickolay Kondratyev

4
EnumUtils.isValidEnum(MyEnum.class, myValue)sử dụng một logic tương tự và IMO thật có ý nghĩa khi thêm toàn bộ thư viện cho nhiệm vụ tầm thường này
Vikramjit Roy

37

Nếu bạn đang sử dụng Java 1.8, bạn có thể chọn Stream + Lambda để thực hiện điều này:

public enum Period {
    DAILY, WEEKLY
};

//This is recommended
Arrays.stream(Period.values()).anyMatch((t) -> t.name().equals("DAILY1"));
//May throw java.lang.IllegalArgumentException
Arrays.stream(Period.values()).anyMatch(Period.valueOf("DAILY")::equals);

19

Guavas Enums có thể là bạn của bạn

Giống như ví dụ này:

enum MyData {
    ONE,
    TWO
}

@Test
public void test() {

    if (!Enums.getIfPresent(MyData.class, "THREE").isPresent()) {
        System.out.println("THREE is not here");
    }
}

18

Thậm chí còn tốt hơn:

enum choices {
   a1, a2, b1, b2;

  public static boolean contains(String s)
  {
      for(choices choice:values())
           if (choice.name().equals(s)) 
              return true;
      return false;
  } 

};

Cảm ơn giải pháp lạc quan
Parth Patel

11

Trước tiên, bạn có thể chuyển đổi enum thành Danh sách và sau đó sử dụng danh sách chứa phương thức

enum Choices{A1, A2, B1, B2};

List choices = Arrays.asList(Choices.values());

//compare with enum value 
if(choices.contains(Choices.A1)){
   //do something
}

//compare with String value
if(choices.contains(Choices.valueOf("A1"))){
   //do something
}

Đây phải là câu trả lời được chấp nhận. Chuyển đổi sang danh sách là cách sạch nhất để làm điều đó. Nó bỏ qua toàn bộ (bên) thảo luận về "sử dụng ngoại lệ hợp lý" trong Java trong các câu trả lời khác ở đây.
Manuel

Ném IllegalArgumentException nếu giá trị không tồn tại.
mkyong

10

Một vài thư viện đã được đề cập ở đây, nhưng tôi nhớ cái mà tôi thực sự đang tìm kiếm: Mùa xuân!

ObjectUtils # chứaConstant không phân biệt chữ hoa chữ thường, nhưng có thể nghiêm ngặt nếu bạn muốn. Nó được sử dụng như thế này:

if(ObjectUtils.containsConstant(Choices.values(), "SOME_CHOISE", true)){
// do stuff
}

Lưu ý: Tôi đã sử dụng phương pháp quá tải ở đây để trình bày cách sử dụng kiểm tra phân biệt chữ hoa chữ thường. Bạn có thể bỏ qua boolean để có hành vi không nhạy cảm.

Tuy nhiên, hãy cẩn thận với số tiền lớn, vì họ không sử dụng triển khai Bản đồ như một số ...

Như một phần thưởng, nó cũng cung cấp một biến thể không phân biệt chữ hoa của valueOf: ObjectUtils # caseInsensitiveValueOf


9

Vài giả định:
1) Không thử / bắt, vì đây là
phương pháp kiểm soát luồng đặc biệt 2) Phương pháp 'chứa' phải nhanh chóng, vì nó thường chạy nhiều lần.
3) Không gian không giới hạn (phổ biến cho các giải pháp thông thường)

import java.util.HashSet;
import java.util.Set;

enum Choices {
    a1, a2, b1, b2;

    private static Set<String> _values = new HashSet<>();

    // O(n) - runs once
    static{
        for (Choices choice : Choices.values()) {
            _values.add(choice.name());
        }
    }

    // O(1) - runs several times
    public static boolean contains(String value){
        return _values.contains(value);
    }
}

7

Bạn có thể sử dụng cái này

YourEnum {A1, A2, B1, B2}

boolean contains(String str){ 
    return Sets.newHashSet(YourEnum.values()).contains(str);
}                                  

Cập nhật được đề xuất bởi @ wightwulf1944 được kết hợp để làm cho giải pháp hiệu quả hơn.


4
Việc thực hiện này là không hiệu quả. Điều này lặp qua các giá trị enum để tạo một tập hợp mới, sau đó tạo một luồng, lặp lại thông qua tập kết quả. Điều này có nghĩa là Tập hợp và Luồng mới được tạo mỗi khi hàm được gọi và sử dụng stream()trên tập có nghĩa là bạn đang lặp lại trên mỗi phần tử trong tập thay vì tận dụng hàm băm bên dưới sẽ nhanh hơn. Để cải thiện điều này, tốt nhất là lưu bộ đệm đã tạo và sử dụng contains()phương thức của nó thay thế. Nếu bạn phải có một luồng, sử dụng Arrays.stream()thay thế.
Subaru Tashiro

3

Tôi không nghĩ là có, nhưng bạn có thể làm một cái gì đó như thế này:

enum choices {a1, a2, b1, b2};

public static boolean exists(choices choice) {
   for(choice aChoice : choices.values()) {
      if(aChoice == choice) {
         return true;
      }
   }
   return false;
}

Biên tập:

Vui lòng xem phiên bản này của Richard vì nó phù hợp hơn vì nó sẽ không hoạt động trừ khi bạn chuyển đổi nó sang sử dụng Chuỗi, điều mà Richards thực hiện.


nhưng tôi nghĩ OP muốn kiểm tra một chuỗi?
Richard H

tuyệt haha. Phương pháp này sẽ không hiệu quả vì chúng ta đã biết lựa chọn là trong enums. Sửa đổi của bạn là chính xác hơn.
jluzwick

1
Nếu bạn muốn làm việc trên một số tập hợp con của enum (chứ không phải tên của chúng), tốt hơn là xem Enumset. download.oracle.com/javase/6/docs/api/java/util/EnumSet.html
Yishai

Có lẽ là một câu hỏi ngu ngốc, nhưng tại sao không phải là .values()tài liệu tại download.oracle.com/javase/6/docs/api/java/lang/Enum.html ?
Ẩn danh

Đó là một câu hỏi tuyệt vời. Bạn đúng, nó không tồn tại trong tài liệu và nó cũng không tồn tại trong nguồn Enum. Tôi cho rằng nó có mặt trong một trong những triển khai của Enum hoặc có một số quy tắc JLS cho phép thực hiện. Ngoài ra tất cả các đối tượng Bộ sưu tập đều có điều này và điều này có thể được xem như là một Bộ sưu tập mặc dù nó không nhất thiết phải thực hiện Bộ sưu tập.
jluzwick

3

Luồng Java cung cấp cách thức thanh lịch để làm điều đó

Stream.of(MyEnum.values()).anyMatch(v -> v.name().equals(strValue))

Trả về: true nếu bất kỳ phần tử nào của luồng khớp với giá trị được cung cấp, nếu không thì sai


2

Tại sao không kết hợp trả lời của Pablo với valueOf ()?

public enum Choices
{
    a1, a2, b1, b2;

    public static boolean contains(String s) {
        try {
            Choices.valueOf(s);
            return true;
        } catch (Exception e) {
            return false;
        }
}

Xin đừng. Xem câu trả lời khác, cũ hơn tương đương với câu trả lời của bạn: stackoverflow.com/a/4936872/103412
Torsten

1

Cách tiếp cận này có thể được sử dụng để kiểm tra bất kỳ Enum, bạn có thể thêm nó vào một Utilslớp:

public static <T extends Enum<T>> boolean enumContains(Class<T> enumerator, String value)
{
    for (T c : enumerator.getEnumConstants()) {
        if (c.name().equals(value)) {
            return true;
        }
    }
    return false;
}

Sử dụng theo cách này:

boolean isContained = Utils.enumContains(choices.class, "value");

1

Tôi đã tạo lớp tiếp theo cho xác nhận này

public class EnumUtils {

    public static boolean isPresent(Enum enumArray[], String name) {
        for (Enum element: enumArray ) {
            if(element.toString().equals(name))
                return true;
        }
        return false;
    }

}

ví dụ về việc sử dụng:

public ArrivalEnum findArrivalEnum(String name) {

    if (!EnumUtils.isPresent(ArrivalEnum.values(), name))
        throw new EnumConstantNotPresentException(ArrivalEnum.class,"Arrival value must be 'FROM_AIRPORT' or 'TO_AIRPORT' ");

    return ArrivalEnum.valueOf(name);
}

0

Bạn có thể sử dụng valueOf("a1")nếu bạn muốn tra cứu bằng String


3
nhưng điều đó không thanh lịch .. nếu giá trị không tồn tại, nó sẽ đưa ra một ngoại lệ. vì vậy bạn có thể phải bao quanh nó bằng thử bắt
Kasturi

Điều đó sẽ tạo ra một ngoại lệ nếu giá trị không tồn tại
Richard H

Ít thanh lịch hơn lặp đi lặp lại thông qua các lựa chọn enum tìm kiếm một đối tượng phù hợp?
jprete

0

Nó là một enum, đó là các giá trị không đổi, vì vậy nếu trong câu lệnh chuyển đổi, nó chỉ làm một cái gì đó như thế này:

case: val1
case: val2

Ngoài ra tại sao bạn cần phải biết những gì được khai báo là hằng số?


Đoạn mã này không có trong câu lệnh chuyển đổi đã nói, ở nơi khác. Tôi chỉ đơn giản nói rằng trong trường hợp này, enum là cần thiết vì những người khác đã đề nghị tôi sử dụng ArrayList thay thế.
Jared

@Jared điều đó có ý nghĩa hơn nhiều bây giờ
Woot4Moo

@Jared tuy nhiên điều đó không thực sự quan trọng vì bạn đã biết các giá trị ở đó. Về cơ bản, enum tương đương với list.contains () là MyEnum.MyAw đũaValue
Woot4Moo

0

Với ổi thậm chí còn đơn giản hơn:

boolean isPartOfMyEnum(String myString){

return Lists.newArrayList(MyEnum.values().toString()).contains(myString);

}

Như kioria nói điều này sẽ không hoạt động. MyEnum.values()trả về mảng của các phiên bản MyEnum và MyEnum.value().toString()trả về đại diện chuỗi của đối tượng mảng này (chỉ chuỗi như "[LMyEnum; @ 15b94ed3")
user2137020 2/214

Bạn cần gọi .name()thay vì .toString()(trừ khi bạn ghi đè phương thức toString mặc định). Xem phần này để biết thêm: Sự khác biệt giữa enum .name () và .toString ()
Gaʀʀʏ

0

Điều này kết hợp tất cả các phương pháp từ các phương pháp trước đó và nên có hiệu suất tương đương. Nó có thể được sử dụng cho bất kỳ enum nào, nội tuyến giải pháp "Chỉnh sửa" từ @Richard H và sử dụng Ngoại lệ cho các giá trị không hợp lệ như @bestsss. Sự đánh đổi duy nhất là lớp cần được chỉ định, nhưng điều đó biến điều này thành một lớp hai.

import java.util.EnumSet;

public class HelloWorld {

static enum Choices {a1, a2, b1, b2}

public static <E extends Enum<E>> boolean contains(Class<E> _enumClass, String value) {
    try {
        return EnumSet.allOf(_enumClass).contains(Enum.valueOf(_enumClass, value));    
    } catch (Exception e) {
        return false; 
    }
}

public static void main(String[] args) {
    for (String value : new String[] {"a1", "a3", null}) {
        System.out.println(contains(Choices.class, value));
    }
}

}


0
com.google.common.collect.Sets.newHashSet(MyEnum.values()).contains("myValue")

0

giải pháp để kiểm tra xem giá trị có hiện diện hay không và nhận lại giá trị enum:

protected TradeType getEnumType(String tradeType) {
    if (tradeType != null) {
        if (EnumUtils.isValidEnum(TradeType.class, tradeType)) {
            return TradeType.valueOf(tradeType);
        }
    }
    return null;
}

0

Cái này hoạt động với tôi:

Arrays.asList(YourEnum.values()).toString().contains("valueToCheck");

3
Phiên bản của bạn sẽ trả về đúng ngay cả khi YourEnum chứa "valueToCheckBlaBla", vì "valueToCheck" sẽ xuất hiện trong biểu diễn chuỗi của toàn bộ danh sách.
Nicko

0

Nếu bạn đang sử dụng Java 8 trở lên, bạn có thể làm điều này:

boolean isPresent(String testString){
      return Stream.of(Choices.values()).map(Enum::name).collect(Collectors.toSet()).contains(testString);
}


0

Bạn có thể làm cho nó như là một phương thức chứa:

enum choices {a1, a2, b1, b2};
public boolean contains(String value){
    try{
        EnumSet.allOf(choices.class).contains(Enum.valueOf(choices.class, value));
        return true;
    }catch (Exception e) {
        return false;
    }
}

hoặc bạn chỉ có thể sử dụng nó với khối mã của bạn:

try{
    EnumSet.allOf(choices.class).contains(Enum.valueOf(choices.class, "a1"));
    //do something
}catch (Exception e) {
    //do something else
}

0

bạn cũng có thể sử dụng: com.google.common.base.Enums

Enums.get IfPftime (varEnum. Class, varToLookFor) trả về một Tùy chọn

Enums.get IfPftime (fooEnum. Class, myVariable) .isPftime ()? Enums.get IfPftime (fooEnum. Class, myVariable) .get: fooEnum.OTHERS


0

Tôi sẽ chỉ viết

Arrays.stream(Choice.values()).map(Enum::name).collect(Collectors.toList()).contains("a1");

Enum # bằng chỉ hoạt động với so sánh đối tượng.


-1
public boolean contains(Choices value) {
   return EnumSet.allOf(Choices.class).contains(value);
}

điều đó sẽ không làm việc thiết lập có các đối tượng enum, trong khi bạn đang kiểm tra nó cho một chuỗi.
iTake

bây giờ câu trả lời không phù hợp với câu hỏi, vì nó là về String :)
iTake

-11

enumkhá mạnh mẽ trong Java. Bạn có thể dễ dàng thêm một containsphương thức vào enum của mình (như bạn sẽ thêm một phương thức vào một lớp):

enum choices {
  a1, a2, b1, b2;

  public boolean contains(String s)
  {
      if (s.equals("a1") || s.equals("a2") || s.equals("b1") || s.equals("b2")) 
         return true;
      return false;
  } 

};

ý bạn là sao s.equals("b1") || s.equals("b2")??
Jigar Joshi

3
Đây có lẽ sẽ không phải là cách tốt nhất để làm điều đó vì bạn sẽ phải thêm mới s.equals("xx")cho mỗi enum bạn thêm sau này.
jluzwick

1
Và sẽ có hơn 1000 enum.
Jared

20
Làm thế nào để những người đề xuất giải pháp khủng khiếp như thế này, có được danh tiếng 64K? Tôi ghét phải nghĩ về tất cả các mã
ngu ngốc
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.