2

I have a list which contains Strings ABC:123,abc:123 ;when I am converting it to Set its giving me 2 different elements.Is there a one liner way to convert this List to Set ignoring the case so that my Set contains ABC:123 only.` But if the input List contains ABC:123a4,abc:1234A4 it should give me 2 different elements in the Set : ABC:123a4,ABC:1234A4 I know this can be done spliting the list elements on ":" first and converting the abc to all uppercase and adding them to new list and then the rest.But just wanted to know if there a better way (small lines of code) to do that.Thanks for any brain storming ideas in advance.

List<String> memlist = new ArrayList<String>(Arrays.asList(memberList.split(",")));
Set<String> memberSet = new HashSet<String>(memlist );
memlist = new ArrayList<String>(memberSet);
Bidisha
  • 287
  • 1
  • 5
  • 16

5 Answers5

6

You can use a TreeSet with the String.CASE_INSENSITIVE_ORDER flag set.

    String startingString = "ABC:123,abc:123";
    List<String> caseSensitiveList = Arrays.asList(startingString.split(","));
    Set<String> caseInsensitiveSet = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
    caseInsensitiveSet.addAll(caseSensitiveList);

    for(String caseInsensitiveString : caseInsensitiveSet){
        System.out.println(caseInsensitiveString);
    }

This code, when run, gives me the output:

ABC:123
Mike Elofson
  • 2,017
  • 1
  • 10
  • 16
  • 2
    I wouldn't use a `TreeSet` when sorting is not involved in the problem at all. – Fred Porciúncula Nov 13 '15 at 15:35
  • @ThiagoPorciúncula you need to use a `TreeSet` to use that override, as far as I know. – Mike Elofson Nov 13 '15 at 15:45
  • 1
    @MikeElofson No you don't. You can use a `HashSet` where you just make all elements upper case first, or you can write a new class that wraps a string and ignores case in the equals and hashCode methods. Using a `TreeSet` when it is not needed is bad for performance as the time complexity of all the basic operations is `O(log n)` rather than `O(1)`. – Paul Boddington Nov 13 '15 at 15:50
  • @MikeElofson I understand that. That's why I'd go with a different solution. You're using a `TreeSet` because of this little detail, not because of what a `TreeSet`, in fact, is. With this solution OP would get a slower and (uselessly) sorted `Set`, instead of his original `HashSet`. – Fred Porciúncula Nov 13 '15 at 15:51
  • This is a nice out-of-the-box solution. Forcing clients to do toUpperCase before each insertion is perhaps more efficient but it's also more error prone. You could wrap a HashSet in your own data structure that makes sure this is done upon insertion, but you would either have to A) extend HashSet (and take care to override a lot of methods to maintain the invariant) or B) wrap a HashSet and have to write a lot of forwarding methods. Until an application is benchmarked and it's certain that O(log n) constitutes a bottle neck, I think a TreeSet based one-liner is preferrable. – aioobe Nov 13 '15 at 16:57
  • @aioobe The problem here is that `HashSet` is so inflexible. They allow you to use a custom `Comparator` **or** the natural ordering for a `TreeSet`, but for some inexplicable reason `HashSet` forces you to use `equals` and `hashCode` rather than allowing you to specify the notion of equality you want for the set. I've ranted about this at length before http://stackoverflow.com/questions/27926111/finding-duplicates-in-a-list-ignoring-a-field – Paul Boddington Nov 13 '15 at 17:30
1

replace

memberList.split(",")

with

memberList.toUpperCase().split(",")
SQL Hacks
  • 1,322
  • 1
  • 10
  • 15
1

The cleanest solution is the one suggested by @SQLHacks. But then you said ABC:123a4 must be different from abc:1234A4. I guess the only solution now is to create a wrapper for the String objects and override the equals() and hashCode() method to do what you want, as @PaulBoddington suggested in his comment.


This is what I came up with (edited and improved based on @nafas answer):

public class StringWrapper {

    private String value;

    private String beforeColon;
    private String afterColon;

    private int hash;

    public StringWrapper(String value) {
        this.value = value;

        String[] splitted = value.split(":");
        beforeColon = splitted[0];
        afterColon = splitted[1];
        hash = Objects.hash(beforeColon.toUpperCase(), afterColon);
    }

    public String getValue() {
        return value;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof StringWrapper) {
            StringWrapper other = (StringWrapper) obj;
            return beforeColon.equalsIgnoreCase(other.beforeColon) && afterColon.equals(other.afterColon);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return hash;
    }

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

}

And then:

    // this method is just to help you building a List<StringWrapper> from your String (memberList variable)
    public static List<StringWrapper> split(String string, String regex) {
        List<StringWrapper> list = new ArrayList<>();
        for (String element : string.split(regex)) {
            list.add(new StringWrapper(element));
        }
        return list;
    }

    public static void main(String[] args) {
        String memberList = "ABC:123,abc:123,ABC:123a4,ABC:123A4";

        List<StringWrapper> memlist = new ArrayList<>(split(memberList, ","));
        Set<StringWrapper> memberSet = new HashSet<>(memlist);
        memlist = new ArrayList<StringWrapper>(memberSet);

        for (StringWrapper element : memlist) {
            System.out.println(element);
        }
    }

If you run this, you get as output the following:

ABC:123a4
ABC:123A4
ABC:123

abc:123 is out but ABC:123a4 and ABC:123A4 are both present.

You can make things even easier changing the static split method to create the Set for you. The reason I didn't do that was to make things look familiar to you.

Fred Porciúncula
  • 8,533
  • 3
  • 40
  • 57
0

You can try this Java 8 one liner

Set<String> memberSet = memlist.stream().map(s -> s.toUpperCase()).collect(Collectors.toSet());

It will go through all strings in memlist convert them to uppercase and put them to a set.

That means of course that if your list contains "abc:123" but not "ABC:123", you will still get "ABC:123" in the set.

Manos Nikolaidis
  • 21,608
  • 12
  • 74
  • 82
0

what is wrong with actually creating a little Model class to take care of all possible cases?

final class Model{
 final String firstPart;
 final String secondPart;
 final int hashCode;
  Model(String s){
   String[] splitted=s.split(":");
   firstPart=splitted[0];
   secondPart=splitted[1];
   hashCode=Objects.hash(firstPart.toLowerCase(),secondPart);

  }

  @Override
  public boolean equals(Object o){
   String[] splitted=o.toString().split(":");
   return firstPart.equalsIgnoreCase(splitted[0]) && secondPard.equals(splitted[1]);
  }

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

  @Override
  public String toString(){
    return firstPart+":"+secondPart;
  }
}

now create a set and etc:

Set<Model> set =new HashSet<Model>();
nafas
  • 5,283
  • 3
  • 29
  • 57