Removing duplicates from List which uses generics - java

Problem : I have a utility function which takes in a generic list to remove duplicates, Now when I use it for List<String> the match should case insensitive. The code uses streams (Java 8+) and i want to keep it that way.
Note : code is in JAVA 8+
Code :
public static <T> List<T> removeDuplicates(List<T> inputList) {
List<T> result = null;
if (inputList != null && inputList.size() > 0) {
result = inputList.parallelStream().distinct().collect(Collectors.toList());
}
return result;
}
EG:
List<String> inputList = new ArrayList<String>();
inputList.add("a");
inputList.add("A");
inputList.add("abc");
inputList.add("ABC");
inputList.add("c");
When we call removeDuplicates(inputList) and print it
Values:
a
abc
c
I don't really care if it choose ABC over abc or A over a but it should be there only once.
Is there an elegant way of solving this issue without doing an instanceof check ?

If the caller knows the type of T at compile time, you can have it pass an optional Comparator<T> to the method, and filter out duplicates using a TreeSet:
public static <T> List<T> removeDuplicates(List<T> inputList) {
// null uses natural ordering
return removeDuplicates(inputList, null);
}
public static <T> List<T> removeDuplicates(List<T> inputList, Comparator<? super T> comparator) {
Set<T> set = new TreeSet<>(comparator);
set.addAll(inputList);
return new ArrayList<>(set);
}
public static void main(String[] args) {
System.out.println(removeDuplicates(Arrays.asList(1, 2, 2, 3)));
System.out.println(removeDuplicates(Arrays.asList("a", "b", "B", "c"), String.CASE_INSENSITIVE_ORDER));
}
Output:
[1, 2, 3]
[a, b, c]

You can extend your method to accept also a function to be applied in map on your stream.
This function will be generic with the same T, so this will solve the need for instanceof. In the String case insensitive example, the function will be String::toLowerCase.
public static <T> List<T> removeDuplicates(List<T> inputList, Function<T,T> function) {
List<T> result = null;
if (inputList != null && inputList.size() > 0) {
result = inputList.parallelStream()
.map(function)
.distinct()
.collect(Collectors.toList());
}
return result;
}
And if you want to keep the same API for the types that don't need it, just add this overload:
public static <T> List<T> removeDuplicates(List<T> inputList) {
return removeDuplicates(inputList, Function.identity());
}

If you want behavior that differs from the default equals behavior you can roll your own String:
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.stream.Collectors;
public class MyString {
private final String value;
public MyString(final String value) {
this.value = value;
}
#Override
public String toString() {
return value;
}
public String getValue() {
return value;
}
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final MyString myString = (MyString) o;
return StringUtils.equalsIgnoreCase(myString.value, value);
}
#Override
public int hashCode() {
return value.toUpperCase().hashCode();
}
public static void main(String... args) {
// args = {aa AA aA bb Bb cc bb CC}
System.out.println(Arrays.stream(args).map(MyString::new).collect(Collectors.toSet()));
// prints: [aa, bb, cc]
}
}

Related

Map collection elements and keep reference to source collection

I'm looking for a way to create a collection, list, set, or map which contains the transformed elements of an original collection and reflects every modification in that collection.
For example if I have a List<Integer> from a third party API and another API is expecting a List<String>. I know I can transform the list like this:
List<Integer> intList = thirdPartyBean.getIntListProperty();
List<String> stringList = intList.stream().map(Integer::toString)
.collect(Collectors.toList());
secondBean.setStringListProperty(stringList);
The problem is, if anything is changed in one of the lists the other one will still reflect the previous state. Let's assume that intList contains [1, 2, 3]:
intList.add(4);
stringList.remove(0);
System.out.println(intList.toString()); // will print: [1, 2, 3, 4]
System.out.println(stringList.toString()); // will print: [2, 3]
// Expected result of both toString(): [2, 3, 4]
So I'm searching for something like List.sublist(from, to) where the result is "backed" by the original list.
I'm thinking of implementing my own list wrapper which is used like this:
List<String> stringList = new MappedList<>(intList, Integer::toString, Integer::valueOf);
The second lambda is for inverting the conversion, to support calls like stringList.add(String).
But before I implement it myself I would like to know if I try to reinvent the wheel - maybe there is already a common solution for this problem?
I would wrap the list in another List with transformers attached.
public class MappedList<S, T> extends AbstractList<T> {
private final List<S> source;
private final Function<S, T> fromTransformer;
private final Function<T, S> toTransformer;
public MappedList(List<S> source, Function<S, T> fromTransformer, Function<T, S> toTransformer) {
this.source = source;
this.fromTransformer = fromTransformer;
this.toTransformer = toTransformer;
}
public T get(int index) {
return fromTransformer.apply(source.get(index));
}
public T set(int index, T element) {
return fromTransformer.apply(source.set(index, toTransformer.apply(element)));
}
public int size() {
return source.size();
}
public void add(int index, T element) {
source.add(index, toTransformer.apply(element));
}
public T remove(int index) {
return fromTransformer.apply(source.remove(index));
}
}
private void test() {
List<Integer> intList = new ArrayList<>(Arrays.asList(1, 2, 3));
List<String> stringList = new MappedList<>(intList, String::valueOf, Integer::valueOf);
intList.add(4);
stringList.remove(0);
System.out.println(intList); // Prints [2, 3, 4]
System.out.println(stringList); // Prints [2, 3, 4]
}
Note that the fromTransformer needs null checking for the input value, if source may contain null.
Now you are not transforming the original list into another one and losing contact with the original, you are adding a transformation to the original list.
I don't know what version of the JDK you are using, but if you are okay with using the JavaFX library you can use ObservableList. You do not need to modify an existing list as ObservableList is a wrapper for java.util.List. Look at extractor in FXCollection for complex Objects. This article has an example of it.
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ListChangeListener.Change;
public class ObservableBiList{
//prevent stackoverflow
private static final AtomicBoolean wasChanged = new AtomicBoolean( false);
public static <T, R> void change( Change< ? extends T> c, ObservableList< R> list, Function< T, R> convert) {
if( wasChanged.get()){
wasChanged.set( false);
return;
}
wasChanged.set( true);
while( c.next()){
if( c.wasAdded() && !c.wasReplaced()){
for( T str : c.getRemoved())
list.add( convert.apply( str));
}else if( c.wasReplaced()){
for( int i=c.getFrom();i<c.getTo();i++)
list.set( i,convert.apply( c.getList().get( i)));
}else if( c.wasRemoved()){
for( T str : c.getRemoved())
list.remove( convert.apply( str));
}
}
System.out.printf( "Added: %s, Replaced: %s, Removed: %s, Updated: %s, Permutated: %s%n",
c.wasAdded(), c.wasReplaced(), c.wasRemoved(), c.wasUpdated(), c.wasPermutated());
}
public static void main( String[] args){
ObservableList< Integer> intList = FXCollections.observableArrayList();
intList.addAll( 1, 2, 3, 4, 5, 6, 7);
ObservableList< String> stringList = FXCollections.observableArrayList();
stringList.addAll( "1", "2", "3", "4", "5", "6", "7");
intList.addListener( ( Change< ? extends Integer> c) -> change( c, stringList, num->Integer.toString( num)));
stringList.addListener( ( Change< ? extends String> c) -> change( c, intList, str->Integer.valueOf( str)));
intList.set( 1, 22);
stringList.set( 3, "33");
System.out.println( intList);
System.out.println( stringList);
}
}
This is exactly the kind of problems that the Observer Pattern solves.
You can create two wrappers, around List<String> and List<Integer> and let first wrapper observe the state of the other one.
public static void main(String... args) {
List<Integer> intList = ObservableList.createBase(new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)));
List<String> stringList = ObservableList.createBase(intList, String::valueOf);
stringList.remove(0);
intList.add(6);
System.out.println(String.join(" ", stringList));
System.out.println(intList.stream().map(String::valueOf).collect(Collectors.joining(" ")));
}
#SuppressWarnings({ "unchecked", "rawtypes" })
private static final class ObservableList<T, E> extends AbstractList<E> {
// original list; only this one could be used to add value
private final List<T> base;
// current snapshot; could be used to remove value;
private final List<E> snapshot;
private final Map<Function<T, ?>, List> cache;
public static <T, E> List<E> createBase(List<T> base) {
Objects.requireNonNull(base);
if (base instanceof ObservableList)
throw new IllegalArgumentException();
return new ObservableList<>(base, null, new HashMap<>());
}
public static <T, R> List<R> createBase(List<T> obsrv, Function<T, R> func) {
Objects.requireNonNull(obsrv);
Objects.requireNonNull(func);
if (!(obsrv instanceof ObservableList))
throw new IllegalArgumentException();
return new ObservableList<>(((ObservableList<T, R>)obsrv).base, func, ((ObservableList<T, R>)obsrv).cache);
}
#SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
private ObservableList(List<T> base, Function<T, E> func, Map<Function<T, ?>, List> cache) {
this.base = base;
snapshot = func != null ? base.stream().map(func).collect(Collectors.toList()) : (List<E>)base;
this.cache = cache;
cache.put(func, snapshot);
}
#Override
public E get(int index) {
return snapshot.get(index);
}
#Override
public int size() {
return base.size();
}
#Override
public void add(int index, E element) {
if (base != snapshot)
super.add(index, element);
base.add(index, (T)element);
cache.forEach((func, list) -> {
if (func != null)
list.add(index, func.apply((T)element));
});
}
#Override
public E remove(int index) {
E old = snapshot.remove(index);
for (List<?> back : cache.values())
if (back != snapshot)
back.remove(index);
return old;
}
}
System.out.println(String.join(" ", stringList));
System.out.println(intList.stream().map(String::valueOf).collect(Collectors.joining(" ")));
}
private static final class ObservableList<E> extends AbstractList<E> {
private final List<List<?>> cache;
private final List<E> base;
public static <E> List<E> create(List<E> delegate) {
if (delegate instanceof ObservableList)
return new ObservableList<>(((ObservableList<E>)delegate).base, ((ObservableList<E>)delegate).cache);
return new ObservableList<>(delegate, new ArrayList<>());
}
public static <T, R> List<R> create(List<T> delegate, Function<T, R> func) {
List<R> base = delegate.stream().map(func).collect(Collectors.toList());
List<List<?>> cache = delegate instanceof ObservableList ? ((ObservableList<T>)delegate).cache : new ArrayList<>();
return new ObservableList<>(base, cache);
}
#SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType")
private ObservableList(List<E> base, List<List<?>> cache) {
this.base = base;
this.cache = cache;
cache.add(base);
}
#Override
public E get(int index) {
return base.get(index);
}
#Override
public int size() {
return base.size();
}
#Override
public void add(int index, E element) {
for (List<?> back : cache)
back.add(index, element);
}
#Override
public E remove(int index) {
E old = base.remove(index);
for (List<?> back : cache)
if (back != base)
back.remove(index);
return old;
}
}
You need to create a wrapper on top of the first list, considering your example, the List<Integer>.
Now if you want List<String> to reflect all the runtime changes done to List<Integer>, you have two solutions.
Don't create initial List<String>, use a method or a wrapper which will always return the transformed values from List<Integer>, so you'll never have a static List<String>.
Create a wrapper around List<Integer>, which should have a reference to List<String>, and override the add(), addAll(), remove() and removeAll() methods. In the overridden methods, change the state of your List<String>.
Another option would be to use the JavaFX ObservableList class which can wrap an existing list with an observable layer on which you can define the operations you want to propagate.
Here is an example which propagates from the string list to the integer list:
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
ObservableList<String> strings = FXCollections.observableList(strList);
strings.addListener((ListChangeListener<String>) change -> {
if(change.next()) {
if (change.wasAdded()) {
change.getAddedSubList().stream().map(Integer::valueOf).forEach(intList::add);
} else if (change.wasRemoved()) {
change.getRemoved().stream().map(Integer::valueOf).forEach(intList::remove);
}
}
});
strList = strings;
strList.add("1");
strList.add("2");
strList.add("2");
System.out.println(intList);
strList.remove("1");
System.out.println(intList);
If you execute this code you will see this output on the console:
[1, 2, 2]
[2, 2]
Try to implement a Thread for this. The example below simulates the context you have presented, but will always have some 100% busy core.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
public class Main {
static List<Integer> intListProperty;
static List<String> stringList;
public static void main(String... args) throws InterruptedException {
Main m = new Main();
m.execute();
}
private void updateAlways(Main main) {
class OneShotTask implements Runnable {
Main main;
OneShotTask(Main main) {
this.main = main;
}
public void run() {
while (main.intListProperty == main.getIntListProperty()) {}
main.intListProperty = getIntListProperty();
main.stringList = main.intListProperty.stream().map(s -> String.valueOf(s)).collect(Collectors.toList());
main.updateAlways(main);
}
}
Thread t = new Thread(new OneShotTask(main));
t.start();
}
public void execute() throws InterruptedException {
System.out.println("Starting monitoring");
stringList = new ArrayList<>();
intListProperty = new ArrayList<>();
intListProperty.add(1);
intListProperty.add(2);
intListProperty.add(3);
updateAlways(this);
while(true) {
Thread.sleep(1000);
System.out.println("\nintListProperty: " + intListProperty.toString()); // will print: [1, 2, 3, 4]
System.out.println("stringList: " + stringList.toString()); // will print: [2, 3]
}
}
// simulated
//thirdPartyBean.getIntListProperty();
private List<Integer> getIntListProperty() {
long timeInMilis = System.currentTimeMillis();
if(timeInMilis % 5000 == 0 && new Random().nextBoolean()) {
Object[] objects = intListProperty.toArray();
// change memory position
intListProperty = new ArrayList<>();
intListProperty = new ArrayList(Arrays.asList(objects));
intListProperty.add(new Random().nextInt());
}
return intListProperty;
}
}
Because of your example I am assuming that the method, you have no access to, only modifies the list and does not access the data itself.
You could use Raw Types.
List list = new ArrayList<Object>();
If you want to access the data you have to convert everything to the desired type.
list.stream().map(String::valueOf).<do_something>.collect(toList())
Not the cleanest solution but might work for you.
I think the cleanest solution would be to implement a wrapper as you already stated.
Example using the System.out:
public static void testInteger(List<Integer> list) {
list.add(3);
list.remove(0);
}
public static void testString(List<String> list) {
list.add("4");
list.remove(0);
}
public static void main(String...args) {
List list = new ArrayList<Object>(Arrays.asList("1", "2"));
testInteger(list);
System.out.println(list.toString()); // will print: [2, 3]
testString(list);
System.out.println(list.toString()); // will print: [3, 4]
}
You always use the same reference, that way you dont need to worry about inconsistencies and its more performant then to always transform the objects.
But something like this would break the code:
public static void main(String...args) {
List list = new ArrayList<Object>(Arrays.asList("1", "2"));
testInteger(list);
System.out.println(list.toString()); // will print: [2, 3]
testString(list);
System.out.println(list.toString()); // will print: [3, 4]
accessData(list); //Will crash
}
public static void accessData(List<Integer> list) {
Integer i = list.get(0); //Will work just fine
i = list.get(1); //Will result in an Class Cast Exception even tho the Method might define it as List<Integer>
}
RawTypes allow you to pass the list to every Method that take a 'List' as argument. But you lose Typesafety, that may or may not be a problem in your case.
As long as the methods only access the elements they added you will have no problem.

Checking for duplicates in an ArrayList with a Comparator

I want to implement a method, that removes duplicates from an ArrayList, by creating a new one without them. I already asked a question about the time complexity of this algorithm, but apparently it doesn't even work. I am pretty sure the error is the equality check but I am not very familiar with Comparators and ArrayLists so any help is appreciated. Here's the code:
public static <T> ArrayList<T> noDups(Comparator<T> cmp, ArrayList<T> l) {
ArrayList<T> noDups = new ArrayList<T>();
for(T o : l) {
if(!isAlreadyInArrayList2(cmp, o, l))
noDups.add(o);
}
return noDups;
}
With iterator:
public static <T> boolean isAlreadyInArrayList(Comparator<T> cmp, T o, ArrayList<T> l) {
Iterator<T> i = l.iterator();
if (o==null) {
while (i.hasNext())
if (i.next()==null)
return true;
} else {
while (i.hasNext())
if (cmp.compare(o, i.next()) == 0)
return true;
}
return false;
}
With a for loop:
public static <T> boolean isAlreadyInArrayList2(Comparator<T> cmp, T o, ArrayList<T> l) {
for(T obj : l) {
if (cmp.compare(o, obj) == 0)
return true;
}
return false;
}
Here is my test which produces an empty ArrayList:
public static void main(String[] args) {
Comparator<Integer> natural = Comparator.<Integer>naturalOrder();
ArrayList<Integer> test = new ArrayList<>(Arrays.asList(1, 5, 4, 2, 2, 0, 1, 4, 2));
System.out.println(test);
ArrayList<Integer> testWithoutDuplicates = noDups(natural,test);
System.out.println(testWithoutDuplicates);
}
You are passing the wrong List to your isAlreadyInArrayList2, change it to the following:
public static <T> ArrayList<T> noDups(Comparator<T> cmp, ArrayList<T> l) {
ArrayList<T> noDups = new ArrayList<T>();
for (T o : l) {
if (!isAlreadyInArrayList2(cmp, o, noDups))
noDups.add(o);
}
return noDups;
}

Predicate generic method

I wrote a Predicate code that takes any Object and tests it for the following conditions:
if Object type is String and contains "k" then it should return true.
if Object type is Integer and greater than 100 then it should return true.
if Object type is Employee which is class and having salary of employee greater than 60000, it should return true.
After writing that Predicate method I wrote the remove method that removes values from list according to Predicate method.
public class ConditionalRemove {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>(Arrays.asList("ramesh", "kushal", "suresh", "kc"));
System.out.println(conditionalRemove(list));
}
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
ConditionCheck<T> cond = new ConditionCheck<>();
for (T t : list) {
if (cond.test(t)) {
list.remove(t);
}
}
return list;
}
static class ConditionCheck<T> implements Predicate<T> {
#Override
public boolean test(T t) {
if (t instanceof String) {
return (((String) t).contains("k"));
} else if (t instanceof Integer) {
return ((int) t > 100);
} else if (t instanceof Employee) {
return ((int) ((Employee) t).getSalary() < 60000);
}
return true;
}
}
}
After compiling this code I found Exception in thread "main" java.util.ConcurrentModificationException
The issue is you are updating the list when you are iterating over that. The issue can be fixed by updating code as
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
ConditionCheck<T> cond = new ConditionCheck<>();
Iterator it = list.iterator();
while(it.hasNext())
{
it.next();
if (cond.test(t)) {
it.remove();
}
}
return list;
}
Since you're using Java 8, a functional approach would be to create a new filtered list:
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
return list.stream()
.filter(new ConditionCheck<>())
.collect(Collectors.toList());
}
You can even replace the static inner class by just a method:
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
return list.stream()
.filter(ConditionalRemove::test)
.collect(Collectors.toList());
}
private static <T> boolean test(T t) {
// your predicate implementation...
}
Don't reinvent the wheel: Use Collection#removeIf():
public static <T> List<T> conditionalRemove(ArrayList<T> list) {
list.removeIf(new ConditionCheck<>());
return list;
}
At one line, this is hardly worth the effort of creating a method to call... just make the single line call in-line:
public static void main(String[] args) {
List<String> list = new ArrayList<>(Arrays.asList("ramesh", "kushal", "suresh", "kc"));
list.removeIf(new ConditionCheck<>());
System.out.println(list);
}
Some data structures throws the java.util.ConcurrentModificationException when you modify them during an iteration, in order to do that with success you need use a synchronized structure such as "CopyOnWriteArrayList", this is the java doc reference
Hope this can help you!
Regards.

How to make a set backed by a map?

There is a method in the Collections class.
Set<E> Collections.newSetFromMap(<backing map>)
What does it mean by the backing map and the set backed by a map?
Perhaps it would be illuminating to look at the implementation:
private static class SetFromMap<E> extends AbstractSet<E>
implements Set<E>, Serializable
{
private final Map<E, Boolean> m; // The backing map
private transient Set<E> s; // Its keySet
SetFromMap(Map<E, Boolean> map) {
if (!map.isEmpty())
throw new IllegalArgumentException("Map is non-empty");
m = map;
s = map.keySet();
}
public void clear() { m.clear(); }
public int size() { return m.size(); }
public boolean isEmpty() { return m.isEmpty(); }
public boolean contains(Object o) { return m.containsKey(o); }
public boolean remove(Object o) { return m.remove(o) != null; }
public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
public Iterator<E> iterator() { return s.iterator(); }
public Object[] toArray() { return s.toArray(); }
public <T> T[] toArray(T[] a) { return s.toArray(a); }
public String toString() { return s.toString(); }
public int hashCode() { return s.hashCode(); }
public boolean equals(Object o) { return o == this || s.equals(o); }
public boolean containsAll(Collection<?> c) {return s.containsAll(c);}
public boolean removeAll(Collection<?> c) {return s.removeAll(c);}
public boolean retainAll(Collection<?> c) {return s.retainAll(c);}
// addAll is the only inherited implementation
private static final long serialVersionUID = 2454657854757543876L;
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
stream.defaultReadObject();
s = m.keySet();
}
}
Edit - added explanation:
The map that you provide is used as the m field in this object.
When you add an element e to the set, it adds an entry e -> true to the map.
public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
So this class turns your Map into an object that behaves like a Set by simply ignoring the values that things are mapped to, and just using the keys.
I just made an example code for you
HashMap<String, Boolean> map = new HashMap<String, Boolean>();
Set<String> set = Collections.newSetFromMap(map);
System.out.println(set);
for (int i = 0; i < 10; i++)
map.put("" + i, i % 2 == 0);
System.out.println(map);
System.out.println(set);
and the output
[]
{3=false, 2=true, 1=false, 0=true, 7=false, 6=true, 5=false, 4=true, 9=false, 8=true}
[3, 2, 1, 0, 7, 6, 5, 4, 9, 8]
Simply put, Collections.newSetFromMap uses the provided Map<E> implementation to store the Set<E> elements.
The Set internally uses Map to store the values. Here the backing map refers to the set map which is internally used by the set.
For more information.
http://www.jusfortechies.com/java/core-java/inside-set.php

Make ArrayList element case-insensitive

In my Java application i need to compare two list's element whether it is similar or not.
In short suppose i have two list declared like shown below
List<String> a = new ArrayList<String>();
a.add("one");
a.add("three");
a.add("two");
Collections.sort(a);
List<String> a1 = new ArrayList<String>();
a1.add("ONE");
a1.add("two");
a1.add("THREE");
Collections.sort(a);
If i write a condition for equality it fails as some of list's element is in different case like
if(a.equals(a1)){
System.out.println("equal");
} else{
System.out.println("not equal");
}
It will display result "Not equal"
So please tell me how i can make the list element case-insensitive in Java language only.
Thank and regard
Why not using instead a SortedSet with a case insensitive comparator ?
With the String.CASE_INSENSITIVE_ORDER comparator
Your code is reduced to
Set<String> a = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
a.add("one");
a.add("three");
a.add("two");
Set<String> a1 = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
a1.add("ONE");
a1.add("two");
a1.add("THREE");
And your equals conditions should work without any issue
EDIT modified according to comments. Thanks to all of you to correct me.
You need to use
Collections.sort(a, String.CASE_INSENSITIVE_ORDER);
in order to sort ignoring case, you can use the equalsIgnoreCase method on String to compare to values
You can of course create your own CaseInsensitiveList class, we have a CaseInsensitiveSet & CaseInsensitiveMap in our codebase
You'd have to do that manually:
public boolean equalsIgnoreCase(List<String> l1, List<String> l2) {
if (l1.size() != l2.size()) {
return false;
}
Iterator<String> i1=l1.iterator();
Iterator<String> i2=l2.iterator();
while(i1.hasNext()) {
if (!i1.next().equalsIgnoreCase(i2.next()) {
return false;
}
}
return true;
}
You can also wrap your String into a helper class and implement the equals & compare methods for it.
public class StringWrapper implements Comparable<StringWrapper> {
private String value;
StringWrapper(Strig value) {
this.value = value;
}
#Override boolean equals(Object o) {
returns String.CASE_INSENSITIVE_ORDER.equals(
(StringWrapper) o).value
this.value);
}
#Override int compareTo(StringWrapper sw) {
returns String.CASE_INSENSITIVE_ORDER.compare(
this.value
sw.value);
}
#Override String toString() {
return this.value;
}
#Override int hashCode() {
return this.value.toLowerCase.hashCode();
}
}
And then :
List<StringWrapper> a = new ArrayList<StringWrapper>();
a.add(StringWrapper("one"));
a.add(StringWrapper("TWO"));
a.add(StringWrapper("three"));
Collections.sort(a);
You will need to override the equals() method on the list so it does what you want. Have a look at the current ArrayList.equals() and adapt it so it compares with equalsIgnoreCase instead of equals().
How about writing a Wrapper class to the List you are using, this would avoid to have inconsistent storage of the elements.
public class CaseInsensitiveStringList extends ArrayList<String> {
#Override
public void add(final int index, final String element) {
super.add(index, element.toLowerCase());
}
#Override
public boolean add(final String o) {
return super.add(o.toLowerCase());
}
#Override
public boolean addAll(final Collection<? extends String> c) {
final ArrayList<String> temp = new ArrayList<String>(c.size());
for (final String s : c) {
temp.add(s.toLowerCase());
}
return super.addAll(temp);
}
#Override
public boolean addAll(final int index, final Collection<? extends String> c) {
final ArrayList<String> temp = new ArrayList<String>(c.size());
for (final String s : c) {
temp.add(s.toLowerCase());
}
return super.addAll(index, temp);
}
}
To sort list of Strings ignoring case
Arrays.sort(myArray, Collator.getInstance());

Categories

Resources