user84786 user84786 - 1 year ago 29
Java Question

Collection removeAll ignoring case?

Ok so here is my issue. I have to HashSet's, I use the Removeall method to delete values that exist in one set from the other.

Prior to calling the method, I obviously add the values to the Sets. I call .toUpperCase() on each String before adding because the values are of different cases in both lists. There is no rhyme or reason to the case.

Once I call RemoveAll, I need to have the original cases back for the values that are left in the Set. Is there an efficient way of doing this without running through the original list and using CompareToIgnoreCase?






After this, create a separate HashSet for each List using toUpperCase() on Strings. Then call RemoveAll.



I need to get the list to look like this again:


Any ideas would be much appreciated. I know it is poor, there should be a standard for the original list but that is not for me to decide. Thank you!

Answer Source

In my original answer, I unthinkingly suggested using a Comparator, but this causes the TreeSet to violate the equals contract and is a bug waiting to happen:

// Don't do this:
Set<String> setA = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);

Set<String> setB = new HashSet<String>();
// Bad code; violates symmetry requirement
System.out.println(setB.equals(setA) == setA.equals(setB));

It is better to use a dedicated type:

public final class CaselessString {
  private final String string;
  private final String normalized;

  private CaselessString(String string, Locale locale) {
    this.string = string;
    normalized = string.toUpperCase(locale);

  @Override public String toString() { return string; }

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

  @Override public boolean equals(Object obj) {
    if (obj instanceof CaselessString) {
      return ((CaselessString) obj).normalized.equals(normalized);
    return false;

  public static CaselessString as(String s, Locale locale) {
    return new CaselessString(s, locale);

  public static CaselessString as(String s) {
    return as(s, Locale.ENGLISH);

  // TODO: probably best to implement CharSequence for convenience

This code is less likely to cause bugs:

Set<CaselessString> set1 = new HashSet<CaselessString>();

Set<CaselessString> set2 = new HashSet<CaselessString>();

System.out.println("1: " + set1);
System.out.println("2: " + set2);
System.out.println("equals: " + set1.equals(set2));

This is, unfortunately, more verbose.