【问题标题】:Compare one String with multiple values in one expression将一个字符串与一个表达式中的多个值进行比较
【发布时间】:2012-04-18 08:13:15
【问题描述】:

我有一个字符串变量 str 和可能的值 val1val2val3

我想使用 if 语句将str 与所有这些值进行比较(大小写相同),例如:

if("val1".equalsIgnoreCase(str)||"val2".equalsIgnoreCase(str)||"val3".equalsIgnoreCase(str))
{
      //remaining code
}

有没有办法避免在一个表达式中使用多个 OR (||) 运算符并比较值?例如,像这样:

 if(("val1" OR "val2" OR "val3").equalsIgnoreCase(str)   //this is only an idea.

【问题讨论】:

标签: java regex string


【解决方案1】:

我找到了更好的解决方案。这可以通过RegEx来实现:

if (str.matches("val1|val2|val3")) {
     // remaining code
}

对于不区分大小写的匹配:

if (str.matches("(?i)val1|val2|val3")) {
     // remaining code
}

【讨论】:

  • matches 函数不适用于所有情况。 @hmjd 的回答为我的情况做了。请参阅Regex doesn't work in String.matches() 了解更多信息。
  • 请注意,根据 Manuel Romeiro 的说法,这是性能最低的解决方案,但我认为它是可读性更强的解决方案之一。
  • 使用正则表达式在长时间运行的情况下效果不佳,请参考我的详细原因,stackoverflow.com/a/62447007/2083529
【解决方案2】:

在 Java 8+ 中,您可以使用 Stream<T>anyMatch(Predicate<? super T>) 之类的东西

if (Stream.of("val1", "val2", "val3").anyMatch(str::equalsIgnoreCase)) {
    // ...
}

【讨论】:

    【解决方案3】:

    您可以将所有要与str 进行比较的字符串存储到一个集合中,并检查该集合是否包含str。将集合中的所有字符串存储为小写,并在查询集合之前将str 转换为小写。例如:

    Set<String> strings = new HashSet<String>();
    strings.add("val1");
    strings.add("val2");
    
    String str = "Val1";
    
    if (strings.contains(str.toLowerCase()))
    {
    }
    

    【讨论】:

    • 您可以使用所需的任何值填充集合。如果str"val",那么在我的答案strings.contains() 中的代码中将返回false
    • 不不,我是说如果有人将 str 值传递为“Val”,它不等于“Val1”、“Val2”、“Val3”。这意味着必须将 str 值传递为“Val”失败..但在你的情况下,这将通过。不满足我的条件。
    • 由于存在equalsIgnoreCase(),因此问题案例无关紧要。如果"Val" 被传递并且strings 包含"val1""val2""val3",那么contains() 将返回false。见ideone.com/LiYKP
    • 哦,你使用 HashSet 来存储 Strings 。好的,我明白了。
    • new TreeSet&lt;&gt;(String.CASE_INSENSITIVE_ORDER)if (strings.contains(str)) { ... }。无需致电String.toLowerCase()
    【解决方案4】:

    使用 apache commons 库中的 StringUtils 的另一种选择(有点类似于上面的 https://stackoverflow.com/a/32241628/6095216):https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/StringUtils.html#equalsAnyIgnoreCase-java.lang.CharSequence-java.lang.CharSequence...-

    if (StringUtils.equalsAnyIgnoreCase(str, "val1", "val2", "val3")) {
      // remaining code
    }
    

    【讨论】:

      【解决方案5】:

      这是一个具有多种选择的性能测试(一些区分大小写,另一些不区分大小写):

      public static void main(String[] args) {
          // Why 4 * 4:
          // The test contains 3 values (val1, val2 and val3). Checking 4 combinations will check the match on all values, and the non match;
          // Try 4 times: lowercase, UPPERCASE, prefix + lowercase, prefix + UPPERCASE;
          final int NUMBER_OF_TESTS = 4 * 4;
          final int EXCUTIONS_BY_TEST = 1_000_000;
          int numberOfMatches;
          int numberOfExpectedCaseSensitiveMatches;
          int numberOfExpectedCaseInsensitiveMatches;
          // Start at -1, because the first execution is always slower, and should be ignored!
          for (int i = -1; i < NUMBER_OF_TESTS; i++) {
              int iInsensitive = i % 4;
              List<String> testType = new ArrayList<>();
              List<Long> timeSteps = new ArrayList<>();
              String name = (i / 4 > 1 ? "dummyPrefix" : "") + ((i / 4) % 2 == 0 ? "val" : "VAL" )+iInsensitive ;
              numberOfExpectedCaseSensitiveMatches = 1 <= i && i <= 3 ? EXCUTIONS_BY_TEST : 0;
              numberOfExpectedCaseInsensitiveMatches = 1 <= iInsensitive && iInsensitive <= 3 && i / 4 <= 1 ? EXCUTIONS_BY_TEST : 0;
              timeSteps.add(System.currentTimeMillis());
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("List (Case sensitive)");
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  if (Arrays.asList("val1", "val2", "val3").contains(name)) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseSensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("Set (Case sensitive)");
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  if (new HashSet<>(Arrays.asList(new String[] {"val1", "val2", "val3"})).contains(name)) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseSensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("OR (Case sensitive)");
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  if ("val1".equals(name) || "val2".equals(name) || "val3".equals(name)) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseSensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("OR (Case insensitive)");
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  if ("val1".equalsIgnoreCase(name) || "val2".equalsIgnoreCase(name) || "val3".equalsIgnoreCase(name)) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseInsensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("ArraysBinarySearch(Case sensitive)");
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  if (Arrays.binarySearch(new String[]{"val1", "val2", "val3"}, name) >= 0) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseSensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("Java8 Stream (Case sensitive)");
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  if (Stream.of("val1", "val2", "val3").anyMatch(name::equals)) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseSensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("Java8 Stream (Case insensitive)");
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  if (Stream.of("val1", "val2", "val3").anyMatch(name::equalsIgnoreCase)) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseInsensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("RegEx (Case sensitive)");
              // WARNING: if values contains special characters, that should be escaped by Pattern.quote(String)
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  if (name.matches("val1|val2|val3")) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseSensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("RegEx (Case insensitive)");
              // WARNING: if values contains special characters, that should be escaped by Pattern.quote(String)
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  if (name.matches("(?i)val1|val2|val3")) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseInsensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              numberOfMatches = 0;
              testType.add("StringIndexOf (Case sensitive)");
              // WARNING: the string to be matched should not contains the SEPARATOR!
              final String SEPARATOR = ",";
              for (int j = 0; j < EXCUTIONS_BY_TEST; j++) {
                  // Don't forget the SEPARATOR at the begin and at the end!
                  if ((SEPARATOR+"val1"+SEPARATOR+"val2"+SEPARATOR+"val3"+SEPARATOR).indexOf(SEPARATOR + name + SEPARATOR)>=0) {
                      numberOfMatches++;
                  }
              }
              if (numberOfMatches != numberOfExpectedCaseSensitiveMatches) {
                  throw new RuntimeException();
              }
              timeSteps.add(System.currentTimeMillis());
      
              //-----------------------------------------
              StringBuffer sb = new StringBuffer("Test ").append(i)
                      .append("{ name : ").append(name)
                      .append(", numberOfExpectedCaseSensitiveMatches : ").append(numberOfExpectedCaseSensitiveMatches)
                      .append(", numberOfExpectedCaseInsensitiveMatches : ").append(numberOfExpectedCaseInsensitiveMatches)
                      .append(" }:\n");
              for (int j = 0; j < testType.size(); j++) {
                  sb.append(String.format("    %4d ms with %s\n", timeSteps.get(j + 1)-timeSteps.get(j), testType.get(j)));
              }
              System.out.println(sb.toString());
          }
      }
      

      输出(只有最坏的情况,即必须检查所有没有匹配的元素):

      Test 4{ name : VAL0, numberOfExpectedCaseSensitiveMatches : 0, numberOfExpectedCaseInsensitiveMatches : 0 }:
        43 ms with List (Case sensitive)
       378 ms with Set (Case sensitive)
        22 ms with OR (Case sensitive)
       254 ms with OR (Case insensitive)
        35 ms with ArraysBinarySearch(Case sensitive)
       266 ms with Java8 Stream (Case sensitive)
       531 ms with Java8 Stream (Case insensitive)
      1009 ms with RegEx (Case sensitive)
      1201 ms with RegEx (Case insensitive)
       107 ms with StringIndexOf (Case sensitive)
      

      由 Warpspeed SCP 提供的输出,更改测试以填充循环之外的集合,在要测试的值列表永不更改时模拟代码(并且可以缓存集合)。

      (本次测试与上一次测试的时间不做比较,因为它是在不同的环境下执行的,而只比较同一测试不同策略的时间):

      Test 4{ name : VAL0, numberOfExpectedCaseSensitiveMatches : 0, numberOfExpectedCaseInsensitiveMatches : 0 }:
          26 ms with List (Case sensitive)
          6 ms with Set (Case sensitive)
          12 ms with OR (Case sensitive)
          371 ms with OR (Case insensitive)
          14 ms with ArraysBinarySearch(Case sensitive)
          100 ms with Java8 Stream (Case sensitive)
          214 ms with Java8 Stream (Case insensitive)
          773 ms with RegEx (Case sensitive)
          946 ms with RegEx (Case insensitive)
          37 ms with StringIndexOf (Case sensitive)
      

      【讨论】:

      • 这是一个提取集合声明的基准:pastebin
      • 请注意,随着基准测试的进展,其中一些操作的时间会不断缩短。
      • 感谢新的基准测试,当要测试的值列表永远不会改变(并且集合可以被缓存)时,它可以用于普通代码
      【解决方案6】:

      ArrayUtils 可能会有所帮助。

      ArrayUtils.contains(new String[]{"1", "2"}, "1")
      

      【讨论】:

        【解决方案7】:

        对完全有效的@hmjd's answer 的小改进:您可以使用以下语法:

        class A {
        
          final Set<String> strings = new HashSet<>() {{
            add("val1");
            add("val2");
          }};
        
          // ...
        
          if (strings.contains(str.toLowerCase())) {
          }
        
          // ...
        }
        

        它允许您就地初始化Set

        【讨论】:

          【解决方案8】:

          只需使用 var-args 并编写自己的静态方法:

          public static boolean compareWithMany(String first, String next, String ... rest)
          {
              if(first.equalsIgnoreCase(next))
                  return true;
              for(int i = 0; i < rest.length; i++)
              {
                  if(first.equalsIgnoreCase(rest[i]))
                      return true;
              }
              return false;
          }
          
          public static void main(String[] args)
          {
              final String str = "val1";
              System.out.println(compareWithMany(str, "val1", "val2", "val3"));
          }
          

          【讨论】:

          • 我喜欢这个!我可能忽略了一些东西,但你为什么将next 作为参数包含在内?这也可以是 var-args rest 的一部分,对吧?
          • 通过“下一个”,我强制至少传递 一个 参数。所以你不能做compareWithMany("foo")。这是一个编译时的完整性检查,而不是在运行时处理一个空集来比较。
          【解决方案9】:

          Apache Commons Collection 类。

          StringUtils.equalsAny(CharSequence string, CharSequence...searchStrings)

          所以在你的情况下,它会是

          StringUtils.equalsAny(str, "val1", "val2", "val3");

          【讨论】:

            【解决方案10】:

            从 Java 9 开始,您可以使用以下任一方式

            List.of("val1", "val2", "val3").contains(str.toLowerCase())
            
            Set.of("val1", "val2", "val3").contains(str.toLowerCase());
            

            【讨论】:

              【解决方案11】:

              建议的解决方案有很多,而且大多数都是可行的解决方案。但是我必须在这里补充一点,人们建议使用正则表达式,即str.matches("val1|val2|val3") 是可以的

              1. 如果方法/代码被多次调用,则性能不佳
              2. 它不是 null 安全的

              我建议改用 apache commons lang3 stringUtils StringUtils.equalsAny(str, "val1", "val2", "val3")

              测试:

              public static void main(String[] args) {
                      String var = "val1";
                      long t, t1 = 0, t2 = 0;
              
                      for (int i = 0; i < 1000; i++) {
                          t = System.currentTimeMillis();
                          var.matches("val1|val2|val3");
                          t1 += System.currentTimeMillis() - t;
              
                          t = System.currentTimeMillis();
                          StringUtils.equalsAny(var, "val1", "val2", "val3");
                          t2 += System.currentTimeMillis() - t;
                      }
                      System.out.println("Matches took + " + t1 + " ms\nStringUtils took " + t2 + " ms");
                  }
              

              1000 次迭代后的结果:

              Matches took + 18 ms
              StringUtils took 7 ms
              

              【讨论】:

              • 仅供参考 StringUtils.equalsAny 只是一个 for 字符串和一个 string.equals(var) 以防万一您不想为此导入孔 StringUtils。
              【解决方案12】:

              对于那些来这里进行精确相等检查(不忽略大小写)的人,我发现

              if (Arrays.asList(str1, str2, str3).contains(strToCheck)) {
                  ...
              }
              

              是最简洁的解决方案之一,并且在 Java 7 上可用。

              【讨论】:

              【解决方案13】:

              您可以使用 Collections 框架来实现这一点。将您的所有选项放在集合中,例如 Collection&lt;String&gt; options ;

              然后循环通过它来比较你的字符串和列表元素,如果是,你可以返回一个布尔值 true,否则返回 false。

              【讨论】:

                【解决方案14】:

                请记住,在 Java 中,带引号的 String 仍然是 String 对象。因此,您可以使用 String 函数 contains() 使用此方法测试一系列字符串或整数:

                if ("A C Viking G M Ocelot".contains(mAnswer)) {...}
                

                对于数字来说,它有点复杂,但仍然有效:

                if ("1 4 5 9 10 17 23 96457".contains(String.valueOf(mNumAnswer))) {...} 
                

                【讨论】:

                • 此解决方案返回误报!例如,对于 mAnswer="king",将返回 true,但应该为 false。不要忘记在 mAnswer 前后加上分隔符(以及所有可能匹配的字符串),然后使用“contains”方法
                【解决方案15】:

                既然这个问题已经重新打开,我不妨提出一个enum 解决方案。

                enum ValidValues {
                   VAL1, VAL2, VAL3;
                
                   public static boolean isValid(String input) {
                       return Stream.of(ValidValues.values())
                                    .map(ValidValues::name)
                                    .anyMatch(s -> s.equalsIgnoreCase(input));
                   }
                }
                

                或者你可以只使用带有流语句的

                Stream.of("val1", "val2", "val3")
                      .anyMatch(s -> s.equalsIgnoreCase(str))
                

                如果你只在一个地方使用它。

                【讨论】:

                  【解决方案16】:

                  很抱歉回答这个老问题,对于 Java 8+,我认为最好的解决方案是 Elliott Frisch (Stream.of("str1", "str2", "str3").anyMatches(str::equalsIgnoreCase)) 提供的解决方案,但它似乎缺少最旧版本 Java 的最简单解决方案之一:

                  if(Arrays.asList("val1", "val2", "val3", ..., "val_n").contains(str.toLowerCase())){
                  //...
                  }
                  

                  您可以通过检查变量str 的非空性并在创建后缓存列表来应用一些错误预防措施。

                  // List of lower-case possibilities
                  final List<String> list = Arrays.asList("val1", "val2", "val3", ..., "val_n");
                  for(String str : somethingYouCouldTheReadStringFrom()){
                    if(str != null && list.contains(str.toLowerCase())){
                      //...
                    }
                  }
                  

                  【讨论】:

                  • 如何加快创建新对象(ArrayList)的速度?你是说 ArrayList 比 Arrays.asList 返回的 List 性能更高?
                  • 你说得对,使用 ArrayList 包装没有性能提升:在这两种情况下,contains() 方法都会线性遍历整个列表。我对 ArrayList 做了一个错误的假设:我认为它在内部使用了哈希索引,但是查看源代码,这两种数组都只是查看了内部数组的所有元素,抱歉。我将编辑我之前的答案,感谢您的更正
                  【解决方案17】:
                  !string.matches("a|b|c|d") 
                  

                  对我来说很好。

                  【讨论】:

                  • 请给出解释/理由以获得更好的答案,太短了
                  【解决方案18】:

                  不,没有这种可能性。 不过,可以想象:

                  public static boolean contains(String s, Collection<String>c) {
                      for (String ss : c) {
                         if (s.equalsIgnoreCase(ss)) return true;
                      }
                      return false;
                  }
                  

                  【讨论】:

                  • 对于值得考虑的集合,例如 HashSet,contains() 的实现效率更高。
                  • 是的,但是(可能是一个有争议的问题)equals 和 equalsIgnoreCase 不会产生相同的结果。这当然可以通过将字符串存储为小写并将您要查找的密钥小写来克服,但是,YMMV
                  猜你喜欢
                  • 1970-01-01
                  • 2020-09-03
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-09-18
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-10-05
                  • 1970-01-01
                  相关资源
                  最近更新 更多