35

I want to check if all elements of two sets of String are equal by ignoring the letter's cases.

Set<String> set1 ;
Set<String> set2 ;
.
.
.
if(set1.equals(set2)){ //all elements of set1 are equal to set2 
 //dosomething
}
else{
 //do something else
}

However, this equality check doesn't ignore the cases of the string. Is there some other way of doing that?

nafas
  • 5,283
  • 3
  • 29
  • 57
  • I think we need a little more info. A `String` can only appear once in a `Set`. But is it possible, in your application, for two strings, `s1` and `s2`, to both be in a set if `s1.equalsIgnoreCase(s2)` is `true`? If so, then what's the criteria for two sets being equal if one set does contain two or more equivalent strings? I think the solution is going to depend on nuances like that. – ajb Jul 03 '14 at 16:06

6 Answers6

61

Alternatively you can use TreeSet.

public static void main(String[] args){
    Set<String> s1 = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
    s1.addAll(Arrays.asList(new String[] {"a", "b", "c"}));

    Set<String> s2 = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
    s2.addAll(Arrays.asList(new String[] {"A", "B", "C"}));

    System.out.println(s1.equals(s2));
}
Syam S
  • 8,421
  • 1
  • 26
  • 36
  • 7
    Note that although this works, it is a very dangerous to assume that equality will be based on the Comparator and not on the equals() method. Implementation may change in the future. – Shimi Bandiel Apr 03 '16 at 07:58
  • @ShimiBandiel So long as the comparator is "consistent with equals" then by definition there is no danger. Correct? The Javadoc for `String.CASE_INSENSITIVE_ORDER` don't indicate one way or another. – Gili Oct 13 '20 at 17:39
  • @Gili I meant the the example provided assumes that the TreeSet equals uses the provided Comparator. – Shimi Bandiel Oct 15 '20 at 23:01
  • 6
    @ShimiBandiel I see. In that case your assertion is wrong. The fact that TreeSet uses Comparator for equals is not an implementation detail. It is explicitly guaranteed by the Javadoc and the latter is TreeSet's API contract / specification. Neither Sun nor Oracle ever change the specifications of public APIs. – Gili Oct 17 '20 at 15:58
6

Unfortunately, Java does not let you supply an external "equality comparer": when you use strings, HashSet uses only built-in hashCode and equals.

You can work around this problem by populating an auxiliary HashSet<String> with strings converted to a specific (i.e. upper or lower) case, and then checking the equality on it, like this:

boolean eq = set1.size() == set2.size();
if (eq) {
    Set<String> aux = new HashSet<String>();
    for (String s : set1) {
        aux.add(s.toUpperCase());
    }
    for (String s : set2) {
        if (!aux.contains(s.toUpperCase())) {
            eq = false;
            break;
        }
    }
}
if (eq) {
    // The sets are equal ignoring the case
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 5
    Based on something I started looking into some time ago (but didn't quite finish): I believe that solutions that involve normalizing the strings (such as `toUpperCase`) work if the letters in the `String`s are all in the A-Z or a-z range, or all in the Latin-1 set (0-255). But if the letters may include all letters in the Unicode set, there are some letters that, I believe, screw up attempts at a simple normalization function. – ajb Jul 03 '14 at 16:22
3

Not that I know of.

The best solution I can see, albeit over-engineered, would be to create your custom holder class holding a String instance field (String is final and cannot be inherited).

You can then override equals / hashCode wherein for two String properties equalsIgnoreCase across two instances, equals would return trueand hashCodes would be equal.

This implies:

  • hashCode returns a hash code based on a lower (or upper) cased property's hash code.
  • equals is based on equalsIgnoreCase

    class MyString {
        String s;
    
        MyString(String s) {
            this.s = s;
        }
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((s == null) ? 0 : s.toLowerCase().hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            MyString other = (MyString) obj;
            if (s == null) {
                if (other.s != null)
                    return false;
            }
            else if (!s.equalsIgnoreCase(other.s))
                return false;
            return true;
        }
    
    }
    public static void main(String[] args) {
            Set<MyString> set0 = new HashSet<MyString>(
                Arrays.asList(new MyString[]
                    {
                        new MyString("FOO"), new MyString("BAR")
                    }
                )
            );
            Set<MyString> set1 = new HashSet<MyString>(
                Arrays.asList(new MyString[]
                    {
                        new MyString("foo"), new MyString("bar")
                    }
                )
            );
            System.out.println(set0.equals(set1));
     }
    

Output

true

... as said, over-engineered (but working).

Mena
  • 47,782
  • 11
  • 87
  • 106
  • many thanks, your solution indeed works. someone just posted this interesting page and then removed [This Link].(http://stackoverflow.com/questions/12214576/java-arraylist-check-if-element-exits-here-ignore-the-case/12214608#12214608). This will do me too. – nafas Jul 03 '14 at 16:17
  • @nafas you're welcome. The linked solution actually looks much better than mine :) – Mena Jul 03 '14 at 16:18
  • The problem with this is that, if you already have a Set, you will need to rebuild it. – zakmck Sep 28 '22 at 11:47
2

Untested, but this is the general idea:

public boolean setEqualsIgnoreCase(Set<String> a, Set<String>b)
{
    if (a.size() != b.size()) return false;
    Iterator<String> ai = a.iterator();
    Iterator<String> bi = b.iterator();
    while(ai.hasNext())
    {
         if (!ai.next().equalsIgnoreCase(bi.next())) return false;
    }
    return true;
}
Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
  • This would also compar ethe order of the sets, and the Set interface does not provide any ordering guarantees. – Joakim M. H. Mar 27 '20 at 08:02
  • Also, it is not making use of the efficient contains method, which is very less compared to comparing each element! – A MJ Apr 19 '23 at 09:10
0

I would build something like this (in some form of Java pseudo code):

Set<String> set1;
Set<String> set2;

if (set1.size() != set2.size()) {
  return NOT_EQUAL;
} else {
  Set<String> set3 = new HashSet<String>();
  for (String s: set1) set3.add(s.toUpperCase());
  for (String s: set2) set3.add(s.toUpperCase());
  return set1.size() == set3.size() ? EQUAL : NOT_EQUAL;
}
spa
  • 5,059
  • 1
  • 35
  • 59
-7

You can use a loop and equalsIgnoreCase

testString.equalsIgnoreCase()
Othya
  • 390
  • 1
  • 3
  • 18
  • 3
    the question is about comparing `Set`, while you're referring to a method of `String`. These are not interchangeable, but I guess you could turn it into a proper answer by adding a loop around. – xaizek Jul 04 '14 at 19:27