Compare one String with multiple values in one expression

148,166

Solution 1

I found the better solution. This can be achieved through RegEx:

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

For case insensitive matching:

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

Solution 2

In Java 8+, you might use a Stream<T> and anyMatch(Predicate<? super T>) with something like

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

Solution 3

You could store all the strings that you want to compare str with into a collection and check if the collection contains str. Store all strings in the collection as lowercase and convert str to lowercase before querying the collection. For example:

Set<String> strings = new HashSet<String>();
strings.add("val1");
strings.add("val2");

String str = "Val1";

if (strings.contains(str.toLowerCase()))
{
}

Solution 4

Yet another alternative (kinda similar to https://stackoverflow.com/a/32241628/6095216 above) using StringUtils from the apache commons library: 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
}

Solution 5

Here a performance test with multiples alternatives (some are case sensitive and others case insensitive):

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());
    }
}

Output (only the worse case, that is when have to check all elements without match none):

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)

Output provided by Warpspeed SCP, changing the test to fill the collections outside of the loops, simulationg the code when the list of values to test never change (and the collections can be cached).

(don't compare the time of this test with the previous test, since it was executed on different environment, but compare only the time of different strategies for the same test):

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)
Share:
148,166
kundan bora
Author by

kundan bora

Software Engineer (Java Developer)

Updated on September 11, 2021

Comments

  • kundan bora
    kundan bora over 2 years

    I have one String variable, str with possible values, val1, val2 and val3.

    I want to compare (with equal case) str to all of these values using an if statement, for example:

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

    Is there a way to avoid using multiple OR (||) operators and compare values in one expression? For example, like this:

     if(("val1" OR "val2" OR "val3").equalsIgnoreCase(str)   //this is only an idea.
    
  • Alexei Kaigorodov
    Alexei Kaigorodov about 12 years
    For the collections worth to consider, like HashSet, contains() has much more efficient implementation.
  • hmjd
    hmjd about 12 years
    You can populate the collection with whatever values you require. In case if str was "val" then in the code in my answer strings.contains() would return false.
  • iThink
    iThink about 12 years
    True, but (which may be a moot point), equals and equalsIgnoreCase do not yield the same result. This could of course be overcome by storing the strings as lower case and lowercasing the key you're looking for, but, YMMV
  • kundan bora
    kundan bora about 12 years
    No no i am saying that if someone pass str value as "Val" which is not as equal as "Val1","Val2,"Val3". That mean passing str value as "Val" must be failed.. but in your case this will be pass.Not satisfy my condition.
  • hmjd
    hmjd about 12 years
    From the question case is irrelevant due to presence of equalsIgnoreCase(). If "Val" is passed and strings contains "val1", "val2", and "val3" then contains() will return false. See ideone.com/LiYKP .
  • kundan bora
    kundan bora about 12 years
    Oh you used HashSet to store Strings . OK I got this.
  • Martijn
    Martijn almost 9 years
    I like this one! I'm probably overlooking something, but why do you include next as a parameter? That can just as well be part of the var-args rest, right?
  • Neet
    Neet almost 9 years
    By having 'next' I enforce passing at least one parameter. So you can not do compareWithMany("foo"). It's a compile-time sanity check, instead of having to deal with an empty set to compare against during runtime.
  • sangram parmar
    sangram parmar over 7 years
    Please give explanation / reason for better answer, It is too short
  • Primoz990
    Primoz990 almost 7 years
    The matches function does not work well for all cases. The answer of @hmjd did it for my case. See Regex doesn't work in String.matches() for more info.
  • Venkata Raju
    Venkata Raju over 5 years
    or new TreeSet<>(String.CASE_INSENSITIVE_ORDER). if (strings.contains(str)) { ... }. No need to call String.toLowerCase()
  • David Bradley
    David Bradley almost 4 years
    Be aware this is the least performant solution according to Manuel Romeiro but I think it's one of the more readable solutions.
  • user2083529
    user2083529 almost 4 years
    using regex is not performant in long running cases kindly refer to my detailed reason, stackoverflow.com/a/62447007/2083529
  • Dima Kozhevin
    Dima Kozhevin over 3 years
    It looks like a part of this answer stackoverflow.com/a/60036035/3166697
  • PhoneixS
    PhoneixS about 3 years
    FYI StringUtils.equalsAny is just a for of the strings and a string.equals(var) just in case you don't want to import the hole StringUtils just for this.
  • Warpspeed SCP
    Warpspeed SCP over 2 years
    Here's a benchmark with collection declarations extracted out: pastebin
  • Warpspeed SCP
    Warpspeed SCP over 2 years
    Note how the times for some of these operations keep shrinking as the benchmark progresses.
  • Manuel Romeiro
    Manuel Romeiro over 2 years
    Thanks for that new benchmark, that can be used on normal code when the list of values to test never change (and the collections can be cached)
  • Manuel Romeiro
    Manuel Romeiro over 2 years
    How do you speed up creating a new object (ArrayList)? Are you telling that ArrayList is more performant than the List returned by Arrays.asList?
  • Manuel Romeiro
    Manuel Romeiro over 2 years
    This solution returns false positives! For example for mAnswer= "king", will return true, but should be false. Don't forget to prefix and suffix the mAnswer with the separator (and also the string with all possibilities to match) , and then use the "contains" method
  • Manuel Romeiro
    Manuel Romeiro over 2 years
    @Venkata, the argument of TreeSet constructor it's only for sort porpoises, not for convert "contains" method into case insensitive
  • Marco Carlo Moriggi
    Marco Carlo Moriggi over 2 years
    You're rigth, no performance gain wrapping with an ArrayList: in both cases the contains() method traverses the whole list linearly. I made a wrong supposition with ArrayList: I supposed that internally uses a hash-index, but looking at the source code both kind of arrays simply look through all the elements of the internal array, sorry. I'll edit my previous answer, thanks for the correction