【问题标题】:Case-insensitive matching of a string to a Java enum字符串与 Java 枚举的不区分大小写匹配
【发布时间】:2015-02-04 22:39:52
【问题描述】:

Java 为每个Enum<T> 对象提供了一个valueOf() 方法,因此给定一个enum 类似

public enum Day {
  Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}

可以进行类似的查找

Day day = Day.valueOf("Monday");

如果传递给valueOf() 的字符串与现有的Day 值不匹配(区分大小写),则会抛出IllegalArgumentException

要进行不区分大小写的匹配,可以在Day 枚举中编写自定义方法,例如

public static Day lookup(String day) {
  for (Day d : Day.values()) {
    if (d.name().equalsIgnoreCase(day)) {
      return type;
    }
  }
  return null;
}

是否有任何通用方法,而不使用缓存值或任何其他额外对象,只编写一次像上面这样的静态lookup() 方法(即,不是每个enum),假设values()方法在编译时隐式添加到Enum<E> 类中?

这种“通用”lookup() 方法的签名类似于Enum.valueOf() 方法,即:

public static <T extends Enum<T>> T lookup(Class<T> enumType, String name);

它会为任何enum 完全实现Day.lookup() 方法的功能,而无需为每个enum 重新编写相同的方法。

【问题讨论】:

  • 我相信你知道,但问题出现的原因是你不坚持Java enum naming convention。如果您使用标准大写命名,您只需在查找方法中使用Day.valueOf(day.toUpperCase())
  • 不,无论大小写或其他命名约定如何,都适用相同的要求。用于所有目的的通用方法是请求。 :-)
  • 添加到@kiedysktos 建议(对于其他阅读者来说,这是迄今为止最直接的解决方案),我怀疑 OP 违反了命名约定,因为他们希望 toString 表单只有第一个字母大写就像语法正确的英语一样。您仍然可以通过覆盖 toString 方法来完成此操作:return super.toString().substring(0, 1).toUpperCase() + super.toString().substring(1);

标签: java generics enums


【解决方案1】:

我发现获得特殊的泛型混合有点棘手,但这是可行的。

public static <T extends Enum<?>> T searchEnum(Class<T> enumeration,
        String search) {
    for (T each : enumeration.getEnumConstants()) {
        if (each.name().compareToIgnoreCase(search) == 0) {
            return each;
        }
    }
    return null;
}

例子

public enum Horse {
    THREE_LEG_JOE, GLUE_FACTORY
};

public static void main(String[] args) {
    System.out.println(searchEnum(Horse.class, "Three_Leg_Joe"));
    System.out.println(searchEnum(Day.class, "ThUrSdAy"));
}

【讨论】:

  • 是的,这行得通。感谢您提供指向 getEnumConstants() 方法的指针。我实际上是想避免通过反射调用values() 方法,这是getEnumConstants() 实际上所做的,但显然没有更好的方法。顺便说一句,您可以使用equalsIgnoreCase() 进行字符串比较。
  • 将值缓存到映射一次而不是每次都迭代它们是否值得?例如Map&lt;String, MyEnum&gt; 的值已经小写了。
  • 反射速度足够快,但缓存速度更快,所以是的,它对特定的枚举有意义,或者如果只为几个枚举调用该方法。在问题缓存不适用中描述的用例中,因为lookup 方法是完全通用的。
【解决方案2】:

我认为最简单安全的方法是:

Arrays.stream(Day.values())
    .filter(e -> e.name().equalsIgnoreCase(dayName)).findAny().orElse(null);

或者如果你想使用类对象,那么:

Arrays.stream(enumClass.getEnumConstants())
    .filter(e -> (Enum)e.name().equalsIgnoreCase(dayName)).findAny().orElse(null);

【讨论】:

  • 嗯...values() 方法不适用于enumType 参数,它的类型为Class&lt;T&gt;
  • @PNS 我原以为这是枚举名称。我已更改为使用示例中的枚举。
【解决方案3】:

3.8 版本开始,apache commons-lang EnumUtils 有两种方便的方法:

  • getEnumIgnoreCase(final Class&lt;E&gt; enumClass, final String enumName)
  • isValidEnumIgnoreCase(final Class&lt;E&gt; enumClass, final String enumName)

【讨论】:

  • 我在 maven 存储库中看不到 3.8。
【解决方案4】:

一个通用的解决方案是遵守常量为大写的约定。 (或者在您的特定情况下使用大写的查找字符串)。

public static <E extends Enum<E>> E lookup(Class<E> enumClass,
        String value) {
    String canonicalValue.toUpperCase().replace(' ', '_');
    return Enum<E>.valueOf(enumClass, canonicalValue);
}

enum Day(MONDAY, ...);
Day d = lookup(Day,class, "thursday");

【讨论】:

  • 可能,但这并不总是可能的,因为我们并不总是可以控制谁编写枚举。 :-)
【解决方案5】:

对于 Android 和相对较短的枚举,我执行简单循环并忽略大小写比较名称。

public enum TransactionStatuses {
    public static TransactionStatuses from(String name) {
        for (TransactionStatuses status : TransactionStatuses.values()) {
            if (status.name().equalsIgnoreCase(name)) {
                return status;
            }
        }
        return null;
    }
}

【讨论】:

    【解决方案6】:

    你可以使用Class's getEnumConstants() method,如果Class代表一个枚举,则返回一个包含所有枚举类型的数组,如果不是,则返回null

    如果此 Class 对象不表示枚举类型,则返回此枚举类的元素或 null。

    您增强的 for 循环行如下所示:

    for (T d : enumType.getEnumConstants()) {
    

    【讨论】:

    • 是的,这是一个很好的解决方案。除了 Enum 之外,我还应该查看 Class 类。谢谢。
    【解决方案7】:

    我还没有测试过这个,但是为什么不重载这些方法,就像SO answer中提到的那样

    public enum Regular {
        NONE,
        HOURLY,
        DAILY,
        WEEKLY;
    
        public String getName() {
            return this.name().toLowerCase();
        }    
    }

    【讨论】:

      【解决方案8】:

      我可以相信这一点,或者尚未发布类似的解决方案。我最喜欢这里(绝对不需要“查找”,只需更聪明的valueOf。另外,作为奖励,枚举值都是大写的,因为我们以前的 c++'ers 认为它​​们应该是......

      public enum Day {
          MONDAY("Monday"), 
          TUESDAY("Tuesday"), 
          WEDNESDAY("Wednesday"),
          THURSDAY("Thursday"), 
          FRIDAY("Friday"),
          SATURDAY("Saturday"),
          SUNDAY("Sunday"); 
      
          public static Day valueOfIgnoreCase(String name) {
               return valueOf(name.toUpperCase());
          }
      
          private final String displayName; 
      
          Day(String displayName) {
              this.displayName = displayName;
          }
      
          @Override
          public String toString() {
              return this.displayName; 
          }
      }
      

      然后:

      Day day = Day.valueOfIgnoreCase("mOnDay"); 
      System.out.println(day); 
      
      >>> Monday
      

      【讨论】:

        【解决方案9】:

        它将完全实现 Day.lookup() 的功能 任何枚举的方法,无需重新编写相同的方法 每个枚举。

        也许您可以编写一个实用程序类来执行此操作,如下所示。

        public class EnumUtil {
        
            private EnumUtil(){
                //A utility class
            }
        
            public static <T extends Enum<?>> T lookup(Class<T> enumType,
                                                           String name) {
                for (T enumn : enumType.getEnumConstants()) {
                    if (enumn.name().equalsIgnoreCase(name)) {
                        return enumn;
                    }
                }
                return null;
            }
        
            // Just for testing
            public static void main(String[] args) {
                System.out.println(EnumUtil.lookup(Day.class, "friday"));
                System.out.println(EnumUtil.lookup(Day.class, "FrIdAy"));
            }
        
        }
        
        enum Day {
            Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
        }
        

        如果在 Java 中有一种方法可以让我们通过隐式添加方法来扩展 Enum 类,就像添加 values() 方法一样,但我认为没有办法做到这一点。

        【讨论】:

          【解决方案10】:

          我正在使用这种方式将字符串与 java 枚举进行不区分大小写的匹配 Day[] days = Day.values(); for(Day day: days) { System.out.println("MONDAY".equalsIgnoreCase(day.name())); }

          【讨论】:

          • 你能像其他答案一样进一步解释一下吗?
          • 如果你打印 Day.Monday.name() 这个,会给你“星期一”。
          • 您还能进一步解释“使用方式”是什么意思吗?你能把它放到一个上下文中,就像大多数其他答案一样吗?
          猜你喜欢
          • 2015-10-27
          • 1970-01-01
          • 2014-09-07
          • 2023-02-08
          • 2011-09-17
          • 1970-01-01
          • 2013-05-03
          • 2019-10-23
          • 2014-10-26
          相关资源
          最近更新 更多