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?
Example:
List1:
"BOB"
"Joe"
"john"
"MARK"
"dave"
"Bill"
List2:
"JOE"
"MARK"
"DAVE"
After this, create a separate HashSet for each List using toUpperCase() on Strings. Then call removeAll.
Set1.removeAll(set2);
Set1:
"BOB"
"JOHN"
"BILL"
I need to get the list to look like this again:
"BOB"
"john"
"Bill"
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.
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);
setA.add("hello");
setA.add("Hello");
System.out.println(setA);
Set<String> setB = new HashSet<String>();
setB.add("HELLO");
// 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>();
set1.add(CaselessString.as("Hello"));
set1.add(CaselessString.as("HELLO"));
Set<CaselessString> set2 = new HashSet<CaselessString>();
set2.add(CaselessString.as("hello"));
System.out.println("1: " + set1);
System.out.println("2: " + set2);
System.out.println("equals: " + set1.equals(set2));
This is, unfortunately, more verbose.
It could be done by:
Moving the content of your lists into case-insensitive TreeSets,
then removing all common Strings case-insensitively thanks TreeSet#removeAll(Collection<?> c)
and finally relying on the fact that ArrayList#retainAll(Collection<?> c) will iterate over the elements of the list and for each element it will call contains(Object o) on the provided collection to know whether the value should be kept or not and here as the collection is case-insensitive, we will keep only the Strings that match case-insensitively with what we have in the provided TreeSet instance.
The corresponding code:
List<String> list1 = new ArrayList<>(
Arrays.asList("BOB", "Joe", "john", "MARK", "dave", "Bill")
);
List<String> list2 = Arrays.asList("JOE", "MARK", "DAVE");
// Add all values of list1 in a case insensitive collection
Set<String> set1 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set1.addAll(list1);
// Add all values of list2 in a case insensitive collection
Set<String> set2 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set2.addAll(list2);
// Remove all common Strings ignoring case
set1.removeAll(set2);
// Keep in list1 only the remaining Strings ignoring case
list1.retainAll(set1);
for (String s : list1) {
System.out.println(s);
}
Output:
BOB
john
Bill
NB 1: It is important to have the content of the second list into a TreeSet especially if we don't know the size of it because the behavior of TreeSet#removeAll(Collection<?> c) depends on the size of both collections, if the size of the current collection is strictly bigger than the size of the provided collection, then it will call directly remove(Object o) on the current collection to remove each element, in this case the provided collection could be a list. But if it is the opposite, it will call contains(Object o) on the provided collection to know whether a given element should be removed or not so if it is not an case-insensitive collection, we won't get the expected result.
NB 2: The behavior of the method ArrayList#retainAll(Collection<?> c) described above is the same as the behavior of the default implementation of the method retainAll(Collection<?> c) that we can find in AbstractCollection such that this approach will actually work with any collections whose implementation of retainAll(Collection<?> c) has the same behavior.
You can use a hashmap and use the capital set as keys that map to the mixed case set.
Keys of hashmaps are unique and you can get a set of them using HashMap.keyset();
to retrieve the original case, it's as simple as HashMap.get("UPPERCASENAME").
And according to the documentation:
Returns a set view of the keys
contained in this map. The set is
backed by the map, so changes to the
map are reflected in the set, and
vice-versa. The set supports element
removal, which removes the
corresponding mapping from this map,
via the Iterator.remove, Set.remove,
removeAll, retainAll, and clear
operations. It does not support the
add or addAll operations.
So HashMap.keyset().removeAll will effect the hashmap :)
EDIT: use McDowell's solution. I overlooked the fact that you didn't actually need the letters to be upper case :P
This would be an interesting one to solve using google-collections. You could have a constant Predicate like so:
private static final Function<String, String> TO_UPPER = new Function<String, String>() {
public String apply(String input) {
return input.toUpperCase();
}
and then what you're after could be done someting like this:
Collection<String> toRemove = Collections2.transform(list2, TO_UPPER);
Set<String> kept = Sets.filter(list1, new Predicate<String>() {
public boolean apply(String input) {
return !toRemove.contains(input.toUpperCase());
}
}
That is:
Build an upper-case-only version of the 'to discard' list
Apply a filter to the original list, retaining only those items whose uppercased value is not in the upper-case-only list.
Note that the output of Collections2.transform isn't an efficient Set implementation, so if you're dealing with a lot of data and the cost of probing that list will hurt you, you can instead use
Set<String> toRemove = Sets.newHashSet(Collections2.transform(list2, TO_UPPER));
which will restore an efficient lookup, returning the filtering to O(n) instead of O(n^2).
as far as i know, hashset's use the object's hashCode-method to distinct them from each other.
you should therefore override this method in your object in order to distinct cases.
if you're really using string, you cannot override this method as you cannot extend the String-class.
therefore you need to create your own class containing a string as attribute which you fill with your content. you might want to have a getValue() and setValue(String) method in order to modify the string.
then you can add your own class to the hashmap.
this should solve your problem.
regards
Related
I'm currently trying to create a method that determine if an ArrayList(a2) contains an ArrayList(a1), given that both lists contain duplicate values (containsAll wouldn't work as if an ArrayList contains duplicate values, then it would return true regardless of the quantity of the values)
This is what I have: (I believe it would work however I cannot use .remove within the for loop)
public boolean isSubset(ArrayList<Integer> a1, ArrayList<Integer> a2) {
Integer a1Size= a1.size();
for (Integer integer2:a2){
for (Integer integer1: a1){
if (integer1==integer2){
a1.remove(integer1);
a2.remove(integer2);
if (a1Size==0){
return true;
}
}
}
}
return false;
}
Thanks for the help.
Updated
I think the clearest statement of your question is in one of your comments:
Yes, the example " Example: [dog,cat,cat,bird] is a match for
containing [cat,dog] is false but containing [cat,cat,dog] is true?"
is exactly what I am trying to achieve.
So really, you are not looking for a "subset", because these are not sets. They can contain duplicate elements. What you are really saying is you want to see whether a1 contains all the elements of a2, in the same amounts.
One way to get to that is to count all the elements in both lists. We can get such a count using this method:
private Map<Integer, Integer> getCounter (List<Integer> list) {
Map<Integer, Integer> counter = new HashMap<>();
for (Integer item : list) {
counter.put (item, counter.containsKey(item) ? counter.get(item) + 1 : 1);
}
return counter;
}
We'll rename your method to be called containsAllWithCounts(), and it will use getCounter() as a helper. Your method will also accept List objects as its parameters, rather than ArrayList objects: it's a good practice to specify parameters as interfaces rather than implementations, so you are not tied to using ArrayList types.
With that in mind, we simply scan the counts of the items in a2 and see that they are the same in a1:
public boolean containsAllWithCounts(List<Integer> a1, List<Integer> a2) {
Map<Integer,Integer> counterA1 = getCounter(a1);
Map<Integer,Integer> counterA2 = getCounter(a2);
boolean containsAll = true;
for (Map.Entry<Integer, Integer> entry : counterA2.entrySet ()) {
Integer key = entry.getKey();
Integer count = entry.getValue();
containsAll &= counterA1.containsKey(key) && counterA1.get(key).equals(count);
if (!containsAll) break;
}
return containsAll;
}
If you like, I can rewrite this code to handle arbitrary types, not just Integer objects, using Java generics. Also, all the code can be shortened using Java 8 streams (which I originally used - see comments below). Just let me know in comments.
if you want remove elements from list you have 2 choices:
iterate over copy
use concurrent list implementation
see also:
http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedList-java.util.List-
btw why you don't override contains method ??
here you use simple Object like "Integer" what about when you will be using List< SomeComplexClass > ??
example remove with iterator over copy:
List<Integer> list1 = new ArrayList<Integer>();
List<Integer> list2 = new ArrayList<Integer>();
List<Integer> listCopy = new ArrayList<>(list1);
Iterator<Integer> iterator1 = listCopy.iterator();
while(iterator1.hasNext()) {
Integer next1 = iterator1.next();
Iterator<Integer> iterator2 = list2.iterator();
while (iterator2.hasNext()) {
Integer next2 = iterator2.next();
if(next1.equals(next2)) list1.remove(next1);
}
}
see also this answer about iterator:
Concurrent Modification exception
also don't use == operator to compare objects :) instead use equal method
about use of removeAll() and other similarly methods:
keep in mind that many classes that implements list interface don't override all methods from list interface - so you can end up with unsupported operation exception - thus I prefer "low level" binary/linear/mixed search in this case.
and for comparison of complex classes objects you will need override equal and hashCode methods
f you want to remove the duplicate values, simply put the arraylist(s) into a HashSet. It will remove the duplicates based on equals() of your object.
- Olga
In Java, HashMap works by using hashCode to locate a bucket. Each bucket is a list of items residing in that bucket. The items are scanned, using equals for comparison. When adding items, the HashMap is resized once a certain load percentage is reached.
So, sometimes it will have to compare against a few items, but generally it's much closer to O(1) than O(n).
in short - there is no need to use more resources (memory) and "harness" unnecessary classes - as hash map "get" method gets very expensive as count of item grows.
hashCode -> put to bucket [if many item in bucket] -> get = linear scan
so what counts in removing items ?
complexity of equals and hasCode and used of proper algorithm to iterate
I know this is maybe amature-ish, but...
There is no need to remove the items from both lists, so, just take it from the one list
public boolean isSubset(ArrayList<Integer> a1, ArrayList<Integer> a2) {
for(Integer a1Int : a1){
for (int i = 0; i<a2.size();i++) {
if (a2.get(i).equals(a1Int)) {
a2.remove(i);
break;
}
}
if (a2.size()== 0) {
return true;
}
}
return false;
}
If you want to remove the duplicate values, simply put the arraylist(s) into a HashSet. It will remove the duplicates based on equals() of your object.
I need to check if any of the values in one arraylist is present in another arraylist:
import java.util.ArrayList;
public class SampleCode {
ArrayList<Integer> in = new ArrayList<>();
ArrayList<Integer> is = new ArrayList<>();
public static void main(String[] args) {
new SampleCode().work();
}
public void work(){
in.add(3);in.add(4);in.add(5);
is.add(1);is.add(2);is.add(3);
if(is.containsAll(in)){
System.out.println("It does not contain");
}
}
}
It prints "It does not contain". I need to know if there is a way to compare these two arraylists and if any of the values are present in the other arraylist, it should return false. I know iterating can help. Is there any simple way to do this?
I think you can use
Collections.disjoint
which says
Returns true if the two specified collections have no elements in common.
so it will return false if there are any elements in common.
Another possible solution:
public static boolean containsNone(List<?> list, List<?> of) {
List<?> temp = new ArrayList<Object>(list);
temp.retainAll(of);
return temp.isEmpty();
}
For example:
List<String> ref = Arrays.asList("w1", "w2", "w3");
List<String> ok = Arrays.asList("w4", "w5");
List<String> ko = Arrays.asList("w1", "w5");
System.out.println(containsNone(ok, ref));
System.out.println(containsNone(ko, ref));
Prints:
true
false
Try this one
public void work(){
in.add(3);in.add(4);in.add(5);;
is.add(1);is.add(2);is.add(3);;
ArrayList<Integer> matched = new ArrayList<Integer>(in);
matched.retainAll(is);
if(matched.size()>0){
System.out.println(matched);
}else{
System.out.println("It does not contain");
}
}
Collection.indexOfSubList(List<?> source, List<?> target)
Returns the starting position of the first occurrence of the specified
target list within the specified source list, or -1 if there is no
such occurrence.
More formally, returns the lowest index i such that source.subList(i, i+target.size()).equals(target), or -1 if there is no such index. (Returns -1 if target.size() > source.size().)
This implementation uses the "brute force" technique of scanning over the source list, looking for a match with the target at each location in turn.
Collections.disjoint(Collection<?> c1, Collection<?> c2)
Returns true if the two specified collections have no elements in
common.
Care must be exercised if this method is used on collections that do not comply with the general contract for Collection. Implementations may elect to iterate over either collection and test for containment in the other collection (or to perform any equivalent computation). If either collection uses a nonstandard equality test (as does a SortedSet whose ordering is not compatible with equals, or the key set of an IdentityHashMap), both collections must use the same nonstandard equality test, or the result of this method is undefined.
Care must also be exercised when using collections that have restrictions on the elements that they may contain. Collection implementations are allowed to throw exceptions for any operation involving elements they deem ineligible. For absolute safety the specified collections should contain only elements which are eligible elements for both collections.
Note that it is permissible to pass the same collection in both parameters, in which case the method will return true if and only if the collection is empty.
Throws:
NullPointerException - if one collection contains a null element and null is not an eligible element for the other collection. (optional) NullPointerException - if one collection contains a null element and null is not an eligible element for the other collection. (optional) ClassCastException - if one collection contains an element that is of a type which is ineligible for the other collection. (optional)
Collections.disjoint(list1, list2);
is your answer
see Collections documentation
public boolean isListNotOverlapping(List<Integer> yourList1, List<Integer> yourList2) {
for(Integer i : yourList1) {
if (yourList2.contains(i)) {
return false;
}
}
return true;
}
This is most certainly a noob question, but I haven't been able to find a good answer on Google or here, so I have to ask:
What kinda list should I use in Java, when I just want a value to be added once?
The problem is that I'm doing a web technology project in college (a webshop), and I have this cloud I connect too. I can the request the customer ID´s from those who bought items in my shop. What I want to do is extract these ID´s and add them to a list. But when extracting them I get the ID returned for every item they have bought, so I want a list that can check: "This value is already in this list, do nothing", or "This ID is not in the list, lets add the ID"
Is there a list that can do this, or a way to do it with a list without it getting too complicated?
You want a Set, this is the data structure that prevents duplicates. This is a Collection so you can define a function like so:
public Collection<MyObject> foo()
{
return new HashSet<MyObject>();
}
and at a later time change the return internally to this:
public Collection<MyObject> foo()
{
return new ArrayList<MyObject>();
}
And your API won't break.
A Set contains every value only once.
Though, the problem with HashSet is that the order in which the elements were added gets lost. So if you want to preserve the order of elements, I would suggest using a LinkedHashSet.
With a LinkedHashSet, iterating over the elements will return them in the order they were inserted.
public static void main(String[] args) {
Set<String> hashSet = new HashSet<>();
hashSet.add("first");
hashSet.add("second");
hashSet.add("third");
for (String s : hashSet) {
System.out.println(s); // no particular order
}
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("first");
linkedHashSet.add("second");
linkedHashSet.add("third");
for (String s : linkedHashSet) {
System.out.println(s); // "first", "second", "third"
}
}
public boolean insertRecord(Programmer targetProgrammer, List programmerList) {
boolean flag = false;
for (Programmer p : programmerList){
if (targetProgrammer.getId() == p.getId()) {
return true;
}
}
return flag;
}
// Then when you invoke:
Programmer target = new Programmer(1,"Dev","Java");
if (!insertRecord(target, myList)) {
myList.add(target);
}
What you will be looking for is a Set, as a Set is a Collection that contains no duplicates.
There are a few types that you could use depending on your needs:
HashSet
LinkedHashSet
CopyOnWriteArraySet
EnumSet
TreeSet
ConcurrentSkipListSet
Better use HashSet as it takes care of your problem of unique IDs implicitly. Still better is SortedSet where you can have the unique elements printed in sorted order automatically.
I have an ArrayList with a number of records and one column contains gas names as CO2 CH4 SO2, etc. Now I want to retrieve different gas names(unique) only without repeatation from the ArrayList. How can it be done?
You should use a Set. A Set is a Collection that contains no duplicates.
If you have a List that contains duplicates, you can get the unique entries like this:
List<String> gasList = // create list with duplicates...
Set<String> uniqueGas = new HashSet<String>(gasList);
System.out.println("Unique gas count: " + uniqueGas.size());
NOTE: This HashSet constructor identifies duplicates by invoking the elements' equals() methods.
You can use Java 8 Stream API.
Method distinct is an intermediate operation that filters the stream and allows only distinct values (by default using the Object::equals method) to pass to the next operation.
I wrote an example below for your case,
// Create the list with duplicates.
List<String> listAll = Arrays.asList("CO2", "CH4", "SO2", "CO2", "CH4", "SO2", "CO2", "CH4", "SO2");
// Create a list with the distinct elements using stream.
List<String> listDistinct = listAll.stream().distinct().collect(Collectors.toList());
// Display them to terminal using stream::collect with a build in Collector.
String collectAll = listAll.stream().collect(Collectors.joining(", "));
System.out.println(collectAll); //=> CO2, CH4, SO2, CO2, CH4 etc..
String collectDistinct = listDistinct.stream().collect(Collectors.joining(", "));
System.out.println(collectDistinct); //=> CO2, CH4, SO2
I hope I understand your question correctly: assuming that the values are of type String, the most efficient way is probably to convert to a HashSet and iterate over it:
ArrayList<String> values = ... //Your values
HashSet<String> uniqueValues = new HashSet<>(values);
for (String value : uniqueValues) {
... //Do something
}
you can use this for making a list Unique
ArrayList<String> listWithDuplicateValues = new ArrayList<>();
list.add("first");
list.add("first");
list.add("second");
ArrayList uniqueList = (ArrayList) listWithDuplicateValues.stream().distinct().collect(Collectors.toList());
ArrayList values = ... // your values
Set uniqueValues = new HashSet(values); //now unique
Here's straightforward way without resorting to custom comparators or stuff like that:
Set<String> gasNames = new HashSet<String>();
List<YourRecord> records = ...;
for(YourRecord record : records) {
gasNames.add(record.getGasName());
}
// now gasNames is a set of unique gas names, which you could operate on:
List<String> sortedGasses = new ArrayList<String>(gasNames);
Collections.sort(sortedGasses);
Note: Using TreeSet instead of HashSet would give directly sorted arraylist and above Collections.sort could be skipped, but TreeSet is otherwise less efficent, so it's often better, and rarely worse, to use HashSet even when sorting is needed.
When I was doing the same query, I had hard time adjusting the solutions to my case, though all the previous answers have good insights.
Here is a solution when one has to acquire a list of unique objects, NOT strings.
Let's say, one has a list of Record object. Record class has only properties of type String, NO property of type int.
Here implementing hashCode() becomes difficult as hashCode() needs to return an int.
The following is a sample Record Class.
public class Record{
String employeeName;
String employeeGroup;
Record(String name, String group){
employeeName= name;
employeeGroup = group;
}
public String getEmployeeName(){
return employeeName;
}
public String getEmployeeGroup(){
return employeeGroup;
}
#Override
public boolean equals(Object o){
if(o instanceof Record){
if (((Record) o).employeeGroup.equals(employeeGroup) &&
((Record) o).employeeName.equals(employeeName)){
return true;
}
}
return false;
}
#Override
public int hashCode() { //this should return a unique code
int hash = 3; //this could be anything, but I would chose a prime(e.g. 5, 7, 11 )
//again, the multiplier could be anything like 59,79,89, any prime
hash = 89 * hash + Objects.hashCode(this.employeeGroup);
return hash;
}
As suggested earlier by others, the class needs to override both the equals() and the hashCode() method to be able to use HashSet.
Now, let's say, the list of Records is allRecord(List<Record> allRecord).
Set<Record> distinctRecords = new HashSet<>();
for(Record rc: allRecord){
distinctRecords.add(rc);
}
This will only add the distinct Records to the Hashset, distinctRecords.
Hope this helps.
public static List getUniqueValues(List input) {
return new ArrayList<>(new LinkedHashSet<>(incoming));
}
dont forget to implement your equals method first
If you have an array of a some kind of object (bean) you can do this:
List<aBean> gasList = createDuplicateGasBeans();
Set<aBean> uniqueGas = new HashSet<aBean>(gasList);
like said Mathias Schwarz above, but you have to provide your aBean with the methods hashCode() and equals(Object obj) that can be done easily in Eclipse by dedicated menu 'Generate hashCode() and equals()' (while in the bean Class).
Set will evaluate the overridden methods to discriminate equals objects.
public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.compareToIgnoreCase(s2);
}
};
private Map< String, Animal > _animals = new TreeMap< String, Animal >(ID_IGN_CASE_COMP);
My problem is, how to use method get(id) ignoring the given comparator. I want the map to be order by Case Insensitive but, I want it to be case sensitive when I fetch the values by a given key.
I think the answer is easy. Implement your own comparator that does a case insensitive sort but does NOT return 0 for "A" and "a"... sort them too.
The issue is that your comparator returns 0 for the compare( "A", "a" ) case which means it is the same key as far as the map is concerned.
Use a comparator like:
public final Comparator<String> ID_IGN_CASE_COMP = new Comparator<String>() {
public int compare(String s1, String s2) {
int result = s1.compareToIgnoreCase(s2);
if( result == 0 )
result = s1.compareTo(s2);
return result;
}
};
Then all keys will go in regardless of case and "a" and "A" will still be sorted together.
In other words, get("a") will give you a different value from get("A")... and they will both show up in keySet() iterators. They will just be sorted together.
In a TreeMap, adding two keys a and b (in that order) so that compare(a, b) returns 0 will result in that the latest added entry (b) will overwrite the first one (a).
In your case, this means that there will never be any use for case insensitive get(id).
quoting http://java.sun.com/javase/6/docs/api/java/util/TreeMap.html
Note that the ordering maintained by a sorted map (whether or not an explicit comparator is provided) must be consistent with equals if this sorted map is to correctly implement the Map interface. (See Comparable or Comparator for a precise definition of consistent with equals.) This is so because the Map interface is defined in terms of the equals operation, but a map performs all key comparisons using its compareTo (or compare) method, so two keys that are deemed equal by this method are, from the standpoint of the sorted map, equal. The behavior of a sorted map is well-defined even if its ordering is inconsistent with equals; it just fails to obey the general contract of the Map interface.
This is probably not what you want.
If the map is comparably small and you don't need to fetch the sorted entries very many times, a solution is to use a HashMap (or a TreeMap without explicitly setting the comparator), and sort the entries case-insensitively when you need them ordered.
You'll have to use two separate TreeMaps for that, with the same contents but different comparators.
maybe it'll do the job:
new Comparator<String>(){
public int compare(String s1, String s2)
{
String s1n = s1.toLowerCase();
String s2n = s2.toLowerCase();
if(s1n.equals(s2n))
{
return s1.compareTo(s2);
}
return s1n.compareTo(s2n);
}
};
}
you need a multimap: each entry of this multimap keeps the case insensitive keys and aanother map with the original keys as value.
There are many freely usable implementations of multimaps such as Common Collections, Google Collections, etc
In addition to all the other answers and agreeing, that it is impossible to have a single TreeMap structure with different comparators:
From your question I understand that you have two requirements: the data model shall be case sensitive (you want the case sensitive values when you use get()), the presenter shall be case insensitive (you want an case sensitive ordering, presentation is just an assumption).
Let's assume, we populate the Map with the mappings (aa,obj1), (aA,obj2), (Aa,obj3), (AA,obj4). The iterator will provides the values in the order: (obj4, obj3, obj2, obj1)(*). Now which order do you expect if the map was ordered case-insensitive? All four keys would be equal and the order undefined. Or are you looking for a solution that would resolve the collection {obj1, obj2, obj3, obj4} for the key 'AA'? But that's a different approach.
SO encourages the community to be honest: therefore my advice at this point is to look at your requirement again :)
(*) not tested, assumed that 'A' < 'a' = true.
Use floorEntry and then higherEntry in a loop to find the entries case-insensitively; stop when you find the exact key match.