98

Is there an option to ignore case with .contains() method?

I have an ArrayList of DVD object. Each DVD object has a few elements, one of them is a title. And I have a method that searches for a specific title. It works, but I'd like it to be case insensitive.

naXa stands with Ukraine
  • 35,493
  • 19
  • 190
  • 259
trama
  • 1,271
  • 3
  • 14
  • 24
  • No, there isn't. But if you explain what you're trying to do, we can probably suggest an alternative approach that would do what you need. – DaoWen Apr 05 '13 at 02:04
  • To perform case-insensitive matching in Java, you can use `String.matches` with a regular expression `.*(?i:abcd).*`. Replace `abcd` with your string. – Ryan Schipper Apr 05 '13 at 02:06
  • 1
    possible duplicate of [Is the Contains Method in java.lang.String Case-sensitive?](http://stackoverflow.com/questions/86780/is-the-contains-method-in-java-lang-string-case-sensitive) – james.garriss Jul 23 '15 at 15:52

20 Answers20

232

If you're using Java 8

List<String> list = new ArrayList<>();

boolean containsSearchStr = list.stream().anyMatch("search_value"::equalsIgnoreCase);
Community
  • 1
  • 1
Nivas Mane-Patil
  • 2,341
  • 2
  • 12
  • 3
34

I'm guessing you mean ignoring case when searching in a string?

I don't know any, but you could try to convert the string to search into either to lower or to upper case, then search.

// s is the String to search into, and seq the sequence you are searching for.
bool doesContain = s.toLowerCase().contains(seq);

Edit: As Ryan Schipper suggested, you can also (and probably would be better off) do seq.toLowerCase(), depending on your situation.

Ryan Schipper
  • 1,019
  • 7
  • 8
Spam
  • 601
  • 4
  • 5
  • 11
    probably want `seq.toLowerCase()` in there also ;) – Ryan Schipper Apr 05 '13 at 02:11
  • 6
    This answer is wrong. See http://mattryall.net/blog/2009/02/the-infamous-turkish-locale-bug for an explanation of why you cannot simply invoke `toLowerCase()` to do case-insensitive comparison. – Gili May 30 '17 at 20:07
  • 1
    As requested I considered writing an explanation why I downvoted. I did this because this answer ignores other languages, which is not the way to go. Thanks. – aliopi Dec 18 '17 at 17:21
  • The behaviour of `toLowerCase` changed between Java 6 and 7, see https://stackoverflow.com/questions/23524231/lower-case-of-turkish-character-dotted-i for – aliopi Jan 03 '18 at 10:57
  • You can't simply use `toLowerCase`. What if the Set contains "intervalOverlaps"? lowercasing would fail. – gregturn Jan 03 '19 at 05:17
  • 1
    The answer to this question should be https://stackoverflow.com/a/38435424/ – Prajwal Dec 17 '20 at 14:18
  • (thumbs down) The OP is asking for a #contains of a collection, not a sequence. – emeraldhieu Jun 18 '21 at 08:59
15
private boolean containsIgnoreCase(List<String> list, String soughtFor) {
    for (String current : list) {
        if (current.equalsIgnoreCase(soughtFor)) {
            return true;
        }
    }
    return false;
}
  • 2
    This is similar to the answer for doing the same with ArrayLists http://stackoverflow.com/a/8751546/1815624 – CrandellWS Jan 11 '17 at 07:35
9

In Java 8 you can use the Stream interface:

return dvdList.stream().anyMatch(d -> d.getTitle().equalsIgnoreCase("SomeTitle"));
Micer
  • 8,731
  • 3
  • 79
  • 73
Paul Bonneau
  • 91
  • 1
  • 1
7

You can replace contains() for equalsIgnoreCase using stream() as below

    List<String> names = List.of("One","tWo", "ThrEe", "foUR", "five", "Six", "THREE");
    boolean contains = names.stream().anyMatch(i -> i.equalsIgnoreCase("three"))
Harshad Panmand
  • 410
  • 5
  • 19
  • 2
    Unnecessary wastage of time and memory. We can use 'anyMatch()' function instead. When first matching is found, will not evaluate the stream further – Roushan Kumar Mar 11 '22 at 05:59
5

I know I'm a little late to the party but in Kotlin you can easily use:

fun Collection<String>.containsIgnoreCase(item: String) = any {
    it.equals(item, ignoreCase = true)
}


val list = listOf("Banana")

println(list.contains("banana"))
println(list.containsIgnoreCase("BaNaNa"))
DDsix
  • 1,966
  • 1
  • 18
  • 23
4

This probably isn't the best way for your particular problem, but you can use the String.matches(String regex) method or the matcher equivalent. We just need to construct a regular expression from your prospective title. Here it gets complex.

List<DVD> matchingDvds(String titleFragment) {
    String escapedFragment = Pattern.quote(titleFragment);
    // The pattern may have contained an asterisk, dollar sign, etc.
    // For example, M*A*S*H, directed by Robert Altman.
    Pattern pat = Pattern.compile(escapedFragment, Pattern.CASE_INSENSITIVE);
    List<DVD> foundDvds = new ArrayList<>();
    for (DVD dvd: catalog) {
        Matcher m = pat.matcher(dvd.getTitle());
        if (m.find()) {
            foundDvds.add(dvd);
        }
    }
    return foundDvds;
}

But this is inefficient, and it's being done purely in Java. You would do better to try one of these techniques:

  1. Learn the Collator and CollationKey classes.
  2. If you have no choice but to stay in the Java world, add a method to DVD, boolean matches(String fragment). Have the DVD tell you what it matches.
  3. Use a database. If it supports case-insensitive collations, declare the title column of the DVD table that way. Use JDBC or Hibernate or JPA or Spring Data, whichever you choose.
  4. If the database supports advanced text search, like Oracle, use that.
  5. Back in the Java world, use Apache Lucene and possibly Apache Solr.
  6. Use a language tuned for case-insensitive matches.

If you can wait until Java 8, use lambda expressions. You can avoid the Pattern and Matcher class that I used above by building the regex this way:

   String escapedFragment = Pattern.quote(titleFragment);
   String fragmentAnywhereInString = ".*" + escapedFragment + ".*";
   String caseInsensitiveFragment = "(?i)" + fragmentAnywhereInString;
   // and in the loop, use:
   if(dvd.getTitle().matches(caseInsensitiveFragment)) {
        foundDvds.add(dvd);
    }

But this compiles the pattern too many times. What about lower-casing everything?

if (dvd.getTitle().toLowerCase().contains(titleFragment.toLowerCase()))

Congratulations; you've just discovered the Turkish problem. Unless you state the locale in toLowerCase, Java finds the current locale. And the lower-casing is slow because it has to take into account the Turkish dotless i and dotted I. At least you have no patterns and no matchers.

Eric Jablow
  • 7,874
  • 2
  • 22
  • 29
3

You can't guarantee that you're always going to get String objects back, or that the object you're working with in the List implements a way to ignore case.

If you do want to compare Strings in a collection to something independent of case, you'd want to iterate over the collection and compare them without case.

String word = "Some word";
List<String> aList = new ArrayList<>(); // presume that the list is populated

for(String item : aList) {
    if(word.equalsIgnoreCase(item)) {
        // operation upon successful match
    }
}
Makoto
  • 104,088
  • 27
  • 192
  • 230
3

Kotlin Devs, go with any / none

private fun compareCategory(
        categories: List<String>?,
        category: String
    ) = categories?.any { it.equals(category, true) } ?: false
Anoop M Maddasseri
  • 10,213
  • 3
  • 52
  • 73
2

The intuitive solution to transform both operands to lower case (or upper case) has the effect of instantiating an extra String object for each item which is not efficient for large collections. Also, regular expressions are an order of magnitude slower than simple characters comparison.

String.regionMatches() allows to compare two String regions in a case-insensitive way. Using it, it's possible to write an efficient version of a case-insensitive "contains" method. The following method is what I use; it's based on code from Apache commons-lang:

public static boolean containsIgnoreCase(final String str, final String searchStr) {
    if (str == null || searchStr == null) {
        return false;
    }
    final int len = searchStr.length();
    final int max = str.length() - len;
    for (int i = 0; i <= max; i++) {
        if (str.regionMatches(true, i, searchStr, 0, len)) {
            return true;
        }
    }
    return false;
}
BladeCoder
  • 12,779
  • 3
  • 59
  • 51
2

It's very simple using the power of Kotlin's extension function, this answer may help Java and Kotlin developers.

inline fun List<String>.contains(text: String, ignoreCase: Boolean = false) = this.any { it.equals(text, ignoreCase) }

// Usage
list.contains("text", ignoreCase = true)
Googlian
  • 6,077
  • 3
  • 38
  • 44
1

With a null check on the dvdList and your searchString

    if (!StringUtils.isEmpty(searchString)) {
        return Optional.ofNullable(dvdList)
                       .map(Collection::stream)
                       .orElse(Stream.empty())
                       .anyMatch(dvd >searchString.equalsIgnoreCase(dvd.getTitle()));
      }
ThrowableException
  • 1,168
  • 1
  • 8
  • 29
1

For Java 8+, I recommend to use following library method.

org.apache.commons.lang3.StringUtils

list.stream()
   .filter(text -> StringUtils.containsIgnoreCase(text, textToSearch))
relativelyPrime
  • 151
  • 1
  • 8
1

Option to ignore case with .contains method? Check the below example

boolean contains = employeeTypes.stream().anyMatch(i -> i.equalsIgnoreCase(employeeType));

I added Custom Annotation for validation in my project

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = EmployeeTypeValidator.class)
public @interface ValidateEmployeeType {
public String message() default "Invalid employeeType: It should be either Permanent or Vendor";

Class<?>[] groups() default { };

Class<? extends Payload>[] payload() default { };
}

Validation of EmployeeType

public class EmployeeTypeValidator implements ConstraintValidator<ValidateEmployeeType, String> {

@Override
public boolean isValid(String employeeType, ConstraintValidatorContext constraintValidatorContext) {
    List<String> employeeTypes = Arrays.asList("Permanent", "vendor", "contractual");
    boolean contains = employeeTypes.stream().anyMatch(i -> i.equalsIgnoreCase(employeeType));
    return contains;
    }
}

Entity of Employee

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
    private int empId;
    @NotBlank(message = "firstName shouldn't be null or empty")
    private String firstName;
    @NotBlank(message = "lastName shouldn't be null or empty")
    private String lastName;
    @Past(message = "start shouldn't be before current date")
    @JsonFormat(pattern = "dd-MM-yyyy")
    private Date doj;
    @NotNull(message = "department shouldn't be null")
    @NotEmpty(message = "department shouldn't be empty")
    private String dept;
    @Email(message = "invalid email id")
    private String email;
    @ValidateEmployeeType
    private String employeeType;
}

For Validation, We need Dependency in pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Note: SNAPSHOT, M1, M2, M3, and M4 releases typically WORK IN PROGRESS. The Spring team is still working on them, Recommend NOT using them.

Fazal Haroon
  • 845
  • 2
  • 10
  • 20
0
 private List<String> FindString(String stringToLookFor, List<String> arrayToSearchIn)
 {
     List<String> ReceptacleOfWordsFound = new ArrayList<String>();

     if(!arrayToSearchIn.isEmpty())
     {
         for(String lCurrentString : arrayToSearchIn)
         {
             if(lCurrentString.toUpperCase().contains(stringToLookFor.toUpperCase())
                 ReceptacleOfWordsFound.add(lCurrentString);
         }
     }
  return ReceptacleOfWordsFound;
 }
Mouhamed Ndiaye
  • 1,390
  • 2
  • 13
  • 21
0

For Java 8, You can have one more solution like below

List<String> list = new ArrayList<>();
String searchTerm = "dvd";

if(String.join(",", list).toLowerCase().contains(searchTerm)) {
  System.out.println("Element Present!");
}
Pavan
  • 51
  • 1
  • 2
0

If you are looking for contains & not equals then i would propose below solution. Only drawback is if your searchItem in below solution is "DE" then also it would match

    List<String> list = new ArrayList<>();
    public static final String[] LIST_OF_ELEMENTS = { "ABC", "DEF","GHI" };
    String searchItem= "def";

     if(String.join(",", LIST_OF_ELEMENTS).contains(searchItem.toUpperCase())) {
            System.out.println("found element");
            break;
    }
user1416932
  • 257
  • 3
  • 6
0
public List<DdsSpreadCoreBean>  filteredByGroupName(DdsSpreadCoreBean ddsSpreadFilterBean, List<DdsSpreadCoreBean> spreadHeaderList){

    List<DdsSpreadCoreBean> filteredByGroupName = new ArrayList<>();
    filteredByGroupName = spreadHeaderList.stream().
            filter(s->s.getGroupName()
               .toLowerCase
               .contains(ddsSpreadFilterBean.getGroupName())).collect(Collectors.toList());
    
    return filteredByGroupName;
}
0

I had the same issue but, in my case, im creating the list and searching inside it for an element.

my solution to my issue was to just put the values in lower case in the list and when im searching for a value i just make the searching value in lowercase too to match it from the list.

-1

You can apply little trick over this.
Change all the string to same case: either upper or lower case
For C# Code:

List searchResults = sl.FindAll(s => s.ToUpper().Contains(seachKeyword.ToUpper()));

For Java Code:

import java.util.*; 

class Test
{
    public static void main(String[] args)
    {
        String itemCheck="check";
        ArrayList<String> listItem =new ArrayList<String>();
        listItem.add("Check");
        listItem.add("check");
        listItem.add("CHeck");
        listItem.add("Make");
        listItem.add("CHecK");
        for(String item :listItem)
        {
            if(item.toUpperCase().equals(itemCheck.toUpperCase()))
            {
                System.out.println(item);
            }
        }

    }

}
Dipitak
  • 97
  • 1
  • 1
  • 3