70

I am currently using the contains method belonging to the ArrayList class for making a search. Is there a way to make this search case insensitive in java? I found that in C# it is possible to use OrdinalIgnoreCase. Is there a java equivalent, or another way to do this? Thanks.

John
  • 1,566
  • 6
  • 17
  • 28
  • Do you need to maintain the case of the strings in the list, or could you make them all lower case? – Aaron J Lang Jan 05 '12 at 23:46
  • I need to keep the case the same. – John Jan 05 '12 at 23:52
  • If you also need the String back in the original case, contains() will only help to indicate that it is present in the list. A HashMap could retrieve the original string given a uppercase or lowercase string, and would have a much better search characteristic (but it would not retain the original order of the strings). – Maarten Bodewes Jan 06 '12 at 01:16
  • Thanks to everyone for the input, now I need to decide which solution I am going to work to. Thanks again. – John Jan 06 '12 at 02:10
  • I just can't believe after all these years Java doesn't have an easy way to do this. – Brain2000 Aug 23 '17 at 20:54
  • Kotlin Devs, go with `any / none` https://stackoverflow.com/a/64817036/4694013 – Anoop M Maddasseri Nov 13 '20 at 07:36

20 Answers20

66

You can use this exactly like you'd use any other ArrayList. You can pass this List out to other code, and external code won't have to understand any string wrapper classes.

public class CustomStringList3 extends ArrayList<String> {
    @Override
    public boolean contains(Object o) {
        String paramStr = (String)o;
        for (String s : this) {
            if (paramStr.equalsIgnoreCase(s)) return true;
        }
        return false;
    }
}
Matt Fenwick
  • 48,199
  • 22
  • 128
  • 192
Aaron J Lang
  • 2,048
  • 3
  • 20
  • 27
  • 6
    This assumes that the original case isn't needed after insertion into the List. Which IS possible. It depends on the OPs use case. Alternatively, maintaining a second list in parallel. One with original data, the second with lowercase only. – rfeak Jan 05 '12 at 23:39
  • 2
    @rfeak Very true! Kinda embarrassed I missed that. In hindsight, I'd use a map, with keys of lower case strings, and values of original strings – Aaron J Lang Jan 05 '12 at 23:44
  • @rfeak Went with your second-list idea in the end – Aaron J Lang Jan 06 '12 at 00:14
  • @owlstead Wrapping the string requires two objects as well, the string and the wrapper – Aaron J Lang Jan 06 '12 at 00:18
  • 2
    Just brainstorming now for fun, I'm sure we have good options for the OP. Could you subclass like this, but instead of overriding add() and get(), could you use just override contains() to leverage equalsIgnoreCase() ? The ideal being, same storage usage, keeping the original String contents/order, keeping the String parameter (vs my idea) and simply instantiating a different List implementation. – rfeak Jan 06 '12 at 00:26
  • 1
    @rfeak your a genius! I'll give it go now – Aaron J Lang Jan 06 '12 at 00:29
  • @rfeak How's that? Does the original method iterate over the whole list? I don't won't to drop performance if there's a faster way. – Aaron J Lang Jan 06 '12 at 00:34
  • @AaronJLang - I think that's got it. Seems pretty clean. – rfeak Jan 06 '12 at 00:41
  • @owlstead I agree, but rfeak and I came up with an even better solution with zero overhead. See my revised answer – Aaron J Lang Jan 06 '12 at 00:43
  • 1
    Aaron, if you have different answers for the same question: post them as separate answers please. This is making discussion impossible. That last comment was strictly about the Map solution. Don't be afraid of bad answers, just mark them as such so the user can strike them from the list of possible solutions. – Maarten Bodewes Jan 06 '12 at 00:47
  • @owlstead I never considered posting multiple separate answers to the same question. I'll do this in the future, thank you :) – Aaron J Lang Jan 06 '12 at 00:50
  • 1
    I consider this being VERY bad solution as it breaks the existing contract for List#contains. If the functionality really really really needs to exist on the List instance itself it shall be implemented as new method. i.e. containsIgnoreCase. – Michal Aug 08 '16 at 07:20
52

In Java8, using anyMatch

List<String> list = Arrays.asList("XYZ", "ABC");
String matchingText = "xYz";

boolean isMatched = list.stream().anyMatch(matchingText::equalsIgnoreCase);
mateuscb
  • 10,150
  • 3
  • 52
  • 76
Free-Minded
  • 5,322
  • 6
  • 50
  • 93
34

If you're using Java 8, try:

List<String> list = ...;
String searchStr = ...;
boolean containsSearchStr = list.stream().filter(s -> s.equalsIgnoreCase(searchStr)).findFirst().isPresent();
Josh M
  • 11,611
  • 7
  • 39
  • 49
14

Traditionally, you can develop your own logic to compare strings held by an ArrayList. There may be several ways to do so like the one shown below.

public boolean containsCaseInsensitive(String strToCompare, ArrayList<String>list)
{
    for(String str:list)
    {
        if(str.equalsIgnoreCase(strToCompare))
        {
            return(true);
        }
    }
    return(false);
}

Why shouldn't be used some direct and convenient ways like a SortedSet as shown below with a case insensitive comparator?.

Set<String> a = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);

a.add("A");
a.add("B");
a.add("C");


Set<String> b = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);

b.add("a");
b.add("b");
b.add("c");

System.out.println(b.equals(a));

Would compare two different sets ignoring case and return true, in this particular situation and your comparision would work without any issue.

Lion
  • 18,729
  • 22
  • 80
  • 110
9

Looking at the Java API, there is no such method for contains.

But you could do at least two things:

  1. Override the equals method in your ArrayList object with your own, or equalsIgnoreCase(str)
  2. Write your own contains method, which should iterate through your ArrayList entities, and do a manual check.

    ArrayList<String> list = new ArrayList<String>();
    ...
    containsIgnoreCase("a", list);
    
    public boolean containsIgnoreCase(String str, ArrayList<String> list){
        for(String i : list){
            if(i.equalsIgnoreCase(str))
                return true;
        }
        return false;
    }
    
HeavenAgain
  • 435
  • 1
  • 6
  • 14
7

Assuming you have an ArrayList<String>

About the only way I can think of to do this would be to create a very light wrapper class around a String and override equals and hashcode to ignore case, leveraging equalsIgnoreCase() where possible. Then you would have an ArrayList<YourString>. It's kinda an ugly idea though.

rfeak
  • 8,124
  • 29
  • 28
  • 3
    +1, but I have to disagree with your last statement: Hardly an ugly solution for the application. – Zéychin Jan 05 '12 at 23:40
  • @AaronJLang - As I commented on your answer. That depends if the original case is required or not. Yes, that may be easier. Depends on the use case for the OP. – rfeak Jan 05 '12 at 23:42
  • 1
    @Zéychin - Heh. It's ugly enough that I'd go look in Guava or Commons Lang for a better solution before I did it in my own code. :) – rfeak Jan 05 '12 at 23:43
  • 5
    Not to be a sore loser, but wouldn't extending the List be simpler/more efficient/more compatible than extending/wrapping all the strings? – Aaron J Lang Jan 06 '12 at 00:10
  • @AaronJLang - I think both solutions would get the job done. It's up to OP which they are most comfortable with. As I expressed initially, I'm not even really happy with my own solution :) – rfeak Jan 06 '12 at 00:22
  • You don't really have to define the ArrayList as ArrayList, you can safely declare it as ArrayList and use YourString only as an argument to ArrayList.contains() (since it accepts any Object, not only the generic type specified). This way, you don't have to unwrap ArrayList's objects during regular access. – Seramme Jan 06 '12 at 00:28
  • @Seramme that breaks the symmetry requirement of equals, you should not go that way – Maarten Bodewes Jan 06 '12 at 01:06
  • It would be logical to use this solution and use a HashSet instead of an ArrayList (but only if the order of the items of the list is inconsequential). The refactoring to a Set is also a good reason not to rewrite contains() as it will always search using O(N/2) – Maarten Bodewes Jan 06 '12 at 01:13
4

You can use IterableUtils and Predicate from collections4 (apache).

List<String> pformats= Arrays.asList("Test","tEst2","tEsT3","TST4");

Predicate<String> predicate  = (s) -> StringUtils.equalsIgnoreCase(s, "TEST");
if(IterableUtils.matchesAny(pformats, predicate)) {
    // do stuff
}

IterableUtils(collections4): org.apache.commons.collections4.IterableUtils.html

bpedroso
  • 4,617
  • 3
  • 29
  • 34
2

For java8

list.stream().anyMatch(s -> s.equalsIgnoreCase(yourString))

For < java8

  • as suggested by Aaron J Lang above
  • Or if you know case of your list ( all upper /all lower ) then convert the search string to appropriate case before comparing
Om.
  • 2,532
  • 4
  • 22
  • 22
2

In my case, as all my strings in the ArrayList are in downcase, I just execute the String.toLowerCase() method on the contains() parameter. Like this:

If (yourArrayList.contains (parameterInput.toLowerCase()) {
    // your code here
}

As you can see, you can do the oposite, if yout arrayList has upperCase strings:

If (yourArrayList.contains (parameterInput.toUpperCase ()) {
    // your code here
}

Using this approach, you do not need to override anything. The exception is in the case when your arrayList have a mix of upper and lower cases.

Bruno Morais
  • 1,029
  • 11
  • 12
2

I would make it like so:

public boolean isStringInList(final List<String> myList, final String stringToFind) {
    return myList.stream().anyMatch(s -> s.equalsIgnoreCase(stringToFind));
}  
Tassu
  • 215
  • 3
  • 13
Mauro
  • 21
  • 1
2

The contains method is based on what the equals method of the objects stored in your ArrayList returns. So yes it is possible if you use objects where equals uses a case insensitive comparison.

So you could for example use a class like this (code might still contain some typos)

public class CaseInsensitiveString{
  private final String contents;

  public CaseInsensitiveString( String contents){ this.contents = contents; }

  public boolean equals( Object o ){
    return o != null && o.getClass() == getClass() && o.contents.equalsIgnoreCase( contents);
  }

  public int hashCode(){
    return o.toUpperCase().hashCode();
  }
}
Robin
  • 36,233
  • 5
  • 47
  • 99
  • All charSequences (ie anything where 'case insensitive' makes sense) have a case-sensitive equals() method, don't they? – Aaron J Lang Jan 05 '12 at 23:41
  • 1
    Maybe add a getter and use `o.equalsIgnoreCase(contents)` instead of `o.toUpperCase().equals(contents.toUpperCase())`? `equalsIgnoreCase()` could be better optimized, and does may not require the allocation of objects for both the uppercase strings. – Maarten Bodewes Jan 06 '12 at 02:42
  • @owlstead sure, a getter would be good but wasn't needed to illustrate my point. The `equalsIgnoreCase` is indeed an improvement, and I will edit my response accordingly – Robin Jan 06 '12 at 07:22
2

ArrayList's contains() method checks equality by calling equals() method on the object you provide (NOT the objects in the array). Therefore, a slightly hackish way is to create a wrapper class around the String object, like this:

class InsensitiveStringComparable {
    private final String val;

    public InsensitiveStringComparable(String val) {
        this.val = val;
    }

    @Override
    public boolean equals(Object x) {
        if (x == this)
            return true;
        else if (x == null)
            return false;
        else if (x instanceof InsensitiveStringComparable)
            return ((InsensitiveStringComparable) x).val.equalsIgnoreCase(val);
        else if (x instanceof String)
            /* Asymmetric equals condition */
            return val.equalsIgnoreCase((String) x);
        else
            return false;
    }

    @Override
    public int hashCode() {
        return val.toUpperCase().hashCode();
    }
}

Then you can use it to perform your test. Example "manual" test case:

public class Test {
    public static void main(String[] args) {
        ArrayList<Object> a = new ArrayList<Object>();
        a.add("test");
        System.out.println(a.contains(new InsensitiveStringComparable("TEST")));
        System.out.println(a.contains(new InsensitiveStringComparable("tEsT")));
        System.out.println(a.contains(new InsensitiveStringComparable("different")));
    }
}
Seramme
  • 1,340
  • 7
  • 9
  • 3
    If you overwrite equals you should overwrite hashCode. – Rogel Garcia Jan 05 '12 at 23:58
  • @rogelware True, though if you only use the InsensitiveStringComparable as an internal "throw-away" class specifically for doing case-insensitive ArrayList search, overwriting hashCode is unnecessary as the ArrayList does not use it (it would be another matter if it was supposed to be used with, for example, HashMap) – Seramme Jan 06 '12 at 00:24
  • It will not work if the lists use the String to compare with the InsensitiveStringComparable and not the opposite. – Rogel Garcia Jan 06 '12 at 00:46
  • 1
    @Seramme that may be true but a very obvious optimization would be to put the object into a HashSet or HashMap, breaking the code. Many static validators such as CheckStyle and FindBugs will happily flag this as an issue. On top of this, the contains() method could be rewritten in the future to test for equality of hashCode() before equals(), there is nothing against that. Just don't go there. – Maarten Bodewes Jan 06 '12 at 00:53
  • @owlstead True, very valid arguments. I have edited the original answer to reflect this. – Seramme Jan 06 '12 at 01:26
  • Unfortunately, your equals() method as well as your hashCode() method is not according to spec. Check the answer of Robin for a better implementation. – Maarten Bodewes Jan 06 '12 at 02:34
2

Another solution:

public class IgnorecaseList extends ArrayList<String>{

    @Override
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    } 

    @Override
    public int indexOf(Object o) {
        if(o instanceof String){
            for (int i = 0; i < this.size(); i++) {
                if(((String)o).equalsIgnoreCase(get(i))){
                    return i;
                }
            }
        }
        return -1;
    }
}

contains() method uses indexOf... In this sollution you can also know in where position is the string. list.add("a") -> list.indexOf("A") == 0 or list.indexOf("a") == 0 ..

You should also consider using a Set instead of List.

Nilesh
  • 4,137
  • 6
  • 39
  • 53
Rogel Garcia
  • 1,895
  • 14
  • 16
  • contains() does not *specify* in the API that it uses indexOf(), so you cannot use it for this. If you go the way of contains() and indexOf() you should probably override *both* and hope that no other method will be defined that requires compatibility. This rules it out as a dependable solution, in my opinion. – Maarten Bodewes Jan 06 '12 at 01:52
1

There's no need to for the additional function, the desired results can be achieved by casting the compared strings to either uppercase or lowercase

(I know this has been suggested in the comments, but not thoroughly provided as an answer)

Ex: Ignores case while filtering the contents of a JList based on the input provided from a JTextField:

private ArrayList<String> getFilteredUsers(String filter, ArrayList<User> users) {
    ArrayList<String> filterUsers = new ArrayList<>();
    users.stream().filter((user) -> (user.getUsername().toUpperCase().contains(filter.toUpperCase()))).forEach((user)-> {
        filterUsers.add(user.getUsername());
    });
    this.userList.setListData(filterUsers.toArray());
    return filterUsers;
    /** 
     *  I see the redundancy in returning the object... so even though,
     *  it is passed by reference you could return type void; but because
     *  it's being passed by reference, it's a relatively inexpensive
     *  operation, so providing convenient access with redundancy is just a 
     *  courtesy, much like putting the seat back down. Then, the next
     *  developer with the unlucky assignment of using your code doesn't 
     *  get the proverbially dreaded "wet" seat.
     */
}
JReyn
  • 99
  • 5
1

Don't reinvent the wheel. Use well tested APIs. For your purpose use Apache Commons StringUtils.

From the Javadoc: Compares given string to a CharSequences vararg of searchStrings returning true if the string is equal to any of the searchStrings, ignoring case.

import org.apache.commons.lang3.StringUtils;
...
StringUtils.equalsAnyIgnoreCase(null, (CharSequence[]) null) = false
StringUtils.equalsAnyIgnoreCase(null, null, null)    = true
StringUtils.equalsAnyIgnoreCase(null, "abc", "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", null, "def")  = false
StringUtils.equalsAnyIgnoreCase("abc", "abc", "def") = true
StringUtils.equalsAnyIgnoreCase("abc", "ABC", "DEF") = true
Cengiz
  • 5,375
  • 6
  • 52
  • 77
1

1st way

  List<String> list = Arrays.asList("XYZ", "ABC");
    String matchingText = "XYZ1";
        boolean isMatched = list.stream().anyMatch(matchingText::equalsIgnoreCase);
        System.out.println(isMatched);

2nd way

List<String> list1= Arrays.asList("XYZ", "ABC");
String searchStr = "abC";
boolean containsSearchStr = list1.stream().filter(searchStr::equalsIgnoreCase).findFirst().isPresent();
System.out.println(containsSearchStr);
DeC
  • 2,226
  • 22
  • 42
0

Its a best way to convert your list item into lowercase. After convert you will use contain method. like

List<String> name_list = new ArrayList<>();
name_list.add("A");
name_list.add("B");

Create lowercase list using above created name_list

List<String> name_lowercase_list = new ArrayList<>();
for(int i =0 ; i<name_list.size(); i++){
 name_lowercase_list.add(name_list.get(i).toLowerCase().toString()); 
}

for(int i =0 ; i<name_list.size(); i++){
  String lower_case_name =  name_list.get(i).toLowerCase().toString();
  if(name_list.get(i).contains(your compare item) ||
   name_lowercase_list.get(i).contains(your compare item) ){
     //this will return true
   }
}
OldMcDonald
  • 594
  • 13
  • 36
0

If you don't want to create a new function, you can try this method:

List<String> myList = new ArrayList<String>(); //The list you're checking
String wordToFind = "Test"; //or scan.nextLine() or whatever you're checking

//If your list has mix of uppercase and lowercase letters letters create a copy...
List<String> copyList = new ArrayList<String>();
for(String copyWord : myList){
   copyList.add(copyWord.toLowerCase());
}

for(String myWord : copyList){
   if(copyList.contains(wordToFind.toLowerCase()){
      //do something
   }
}
0

While the question was asked specifically about Java, the same issue exists in Kotlin.

You can use an extension for this that applies to List, Array and ArrayList

fun Array<String>.contains(element: String, ignoreCase: Boolean): Boolean {
  forEach { if (it.equals(element, ignoreCase = ignoreCase)) return true }
  return false
}
DevinM
  • 1,112
  • 1
  • 12
  • 29
-1

By using compareToIgnoreCase, http://docs.oracle.com/javase/1.3/docs/api/java/lang/String.html#compareToIgnoreCase%28java.lang.String%29 you should be able to do what you want!

Jeremy D
  • 4,787
  • 1
  • 31
  • 38