CaseInsensitive search in HashMap - java

I have a map:
static Map<String, String> = getMap(); //getting a map from a config file.
Now in this Map I need to perform a caseInsensitive search, using KEYS. I am NOT putting the values in map, not through put function, but you can think it as a values stored in data base in a key value format, and retrieving it as a Map. I need to do a caseInsentive search.
After researching, using a TreeMap would solve the problem, but not efficient --> O(log n)
or overring the get() method of HashMap, creating my own HashMap. but this would include overrding many methods, and I dont want this much, it not a part of very important code.
Right now I am im storing the values, in lowercase in database, and checking. But it makes it error prone, and not readble, in database.
Can there be a simpler method to do it?

1.) TreeMap extends Map can be an option, but time complexity is O(log n)
final Map<String, Object> map = new TreeMap<String, Object>(String.CASE_INSENSITIVE_ORDER);
2.) You could use CaseInsensitiveMap from Apache's Commons Collections as suggested above.
3.) Creating your own HashMap Class and overriding methods.
public class MyCaseInsensitiveMap extends HashMap<String, String> {
...
put(String key, String value) {
super.put(key.toLowerCase(), value);
}
get(String key) {
super.get(key.toLowercase());
}
}
4.) You need a wrapper class for your String key with a case-insensitive equals() and hashCode() implementation. Use that instead of the String for the Map's key.
example here
** There does not seems to be straight forward library available except for apache commons.
PS: Consolidated from other links available in SO also.

If you are fine with O(1) but utilizing more space, this may help:
class CaseInsensitiveLookupMap {
private Map<String,String> keysMap = new HashMap<String,String>();
private Map<String,String> dataMap;
public CaseInsensitiveLookupMap(Map<String,String> dataMap){
this.dataMap=dataMap;
for(String key: dataMap.keySet()){
keysMap.put(key.toLowerCase(),key);
}
}
public String get(String keyToSearch){
String _key = keysMap.get(keyToSearch.toLowerCase());
if(_key!=null) {
return dataMap.get(_key);
}
return null;
}
}

Related

java - map to store key and multiple type of values

Basically I'd like something like this: Hashmap<String, String/int> a python equivalent to dictionary in java so be able to store key and value pair, only in my case I need to store the value which can be an int or a string. E.g. of value I'd like to store would be:
{"one":1,
"two":"two"}
So, it's not storing multiple values in one key, just multiple type of value with one type of key. One solution would be to use Hashmap<String, Object> and check the Object instance at runtime, but that really feels tricky and you'd have to check all the values. Is there a more proper way?
There is no another way to do it.
"Everything" in Java extends from Object.
You can create a helper class to handle the checking type or even extend HashMap and create your own getValue method, using generics, like following:
public class MyHashMap extends HashMap<String, Object> {
#Nullable
public <V> V getValue(#Nullable String key) {
//noinspection unchecked
return (V) super.get(key);
}
}
And using like this:
MyHashMap map = new MyHashMap();
map.put("one", 1);
map.put("two", "two");
Integer one = map.getValue("one");
String two = map.getValue("two");
Or even:
public void printNumber(Integer number){
// ...
}
printNumber(map.<Integer>getValue("one"));

Set hashMap's value

I am using drools workbench and I have trouble setting a hashmap. I want to avoid adding a method just to add values to the map so I am trying to find a workaround.
When I need to set the value of a list I use:
setList(Arrays.asList("string one", "string two", ...));
I was wondering if such a method exists for hashmaps.
If you are allowed to use 3rd party libs you can use Guava's ImmutableMap
Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");
If you are going to use Map, then definitely you should store the data in from of (Key, Value) pair. Now , in HashMap, you have two methods to store data,
1. put(Object any) - This method takes single object.
2. putAll(Map otherMap) - This method takes some other map, and will add all the elements of that map to yours one.
So If these methods are not suitable for you, then I think you should write your own method to add values. May be you can write as below.
class DroolMap<K,V> extends HashMap<K,V> {
public DroolMap() {
super();
}
public DroolMap(int size) {
super(size);
}
public DroolMap<K, V> add(K key, V value) {
this.put(key, value);
return this;
}
}
class TestDroolMap {
public void testDroolMap() {
DroolMap<String, String> droolMap = new DroolMap<String, String>();
// You can add as many <Key, Value> pairs in one line
droolMap.add("k1", "v1").add("k2", "v2").add("k3", "v3");
}
}

Separate chaining for HashTables in Java

Based on the following code snippet :
Hashtable balance = new Hashtable();
Enumeration names;
String str;
double bal;
balance.put("Zara", new Double(3434.34)); //first entry for Zara
balance.put("Mahnaz", new Double(123.22));
balance.put("Zara", new Double(1378.00)); //second entry for Zara
balance.put("Daisy", new Double(99.22));
balance.put("Qadir", new Double(-19.08));
System.out.println(balance.entrySet());
.
Output : [Qadir=-19.08, Mahnaz=123.22, Daisy=99.22, Zara=1378.0]
Why isn't chaining happening here? When I re-enter with Zara as key the old value is overwritten. I expected it to be added at the end of the Linked List at Zara".hashcode() index.
Does Java use separate chaining only for collision handling?
If I can't use chaining( as I'v tried above) please suggest a common method to do so.
Does Java use separate chaining only for collision handling?
Yes. You can only have one entry per key in a Hashtable (or HashMap, which is what you should probably be using - along with generics). It's a key/value map, not a key/multiple-values map. In the context of a hash table, the term "collision" is usually used for the situation where two unequal keys have the same hash code. They still need to be treated as different keys, so the implementation has to cope with that. That's not the situation you're in.
It sounds like you might want a multi-map, such as one of the ones in Guava. You can then ask a multimap for all values associated with a particular key.
EDIT: If you want to build your own sort of multimap, you'd have something like:
// Warning: completely untested
public final class Multimap<K, V> {
private final Map<K, List<V>> map = new HashMap<>();
public void add(K key, V value) {
List<V> list = map.get(key);
if (list == null) {
list = new ArrayList();
map.put(key, list);
}
list.add(value);
}
public Iterable<V> getValues(K key) {
List<V> list = map.get(key);
return list == null ? Collections.<V>emptyList()
: Collections.unmodifiableList(list);
}
}
Quote from the documentation of Map (which Hashtable is an implementation of):
An object that maps keys to values. A map cannot contain duplicate keys; each key can map to at most one value.
(emphasis mine)
The documentation of put() also says:
If the map previously contained a mapping for the key, the old value is replaced by the specified value
So if you want multiple values associated with a key, use a Map<String, List<Double>> instead of a Map<String, Double>. Guava also has a Multimap, which does what you want without having to deal with Lists explicitely as with a Map<String, List<Double>>.

Is it possible to make some specific keys of a HashMap read-only?

I have 5 Keys which must not be removed/updated. I provide my own methods to add, get and remove keys of this HashMap.
UnmodifiableMap will make ALL the keys read-only, so I can't use that either. I could maintain a List of these read-only keys and whenever add/remove method is called, I can refer this List and prevent the operation. But is there any other better way to achieve this ?
Thanks in advance!
EDIT: I know I can extend HashMap and override the put method. That's similar to what I said in the problem description above (Maintain a List of read-only keys and prevent operations on them). I thought there could be a way to merge an UnmodifiableMap in a HashMap such that the keys from UnmodifiableMap will remain read-only in the new HashMap and the other keys will have all operations supported on them.
As Andre mentions, you can inherit from HashMap or other Map implementations.
Here's an anonymous class quick example, self-contained in a main method:
public static void main(String[] args) {
Map<String, String> myMap = new HashMap<String, String>() {
private static final long serialVersionUID = 6585814488311720276L;
#Override
public String put(String key, String value) {
if (key != null && !key.equalsIgnoreCase("foo")) {
return super.put(key, value);
}
else {
throw new IllegalArgumentException("No foo's allowed!");
}
}
// TODO!
#Override
public void putAll(Map<? extends String, ? extends String> m) {
// TODO Auto-generated method stub
super.putAll(m);
}
};
System.out.println(myMap.put("blah", "blah"));
System.out.println(myMap.put("foo", "blah"));
}
Output
null
Exception in thread "main" java.lang.IllegalArgumentException: No foo's allowed!
at test.Main$1.put(Main.java:18)
at test.Main$1.put(Main.java:1)
at test.Main.main(Main.java:29)
Create a Map that will encapsulate two other maps. One of these map will be an unmodifiable map and will contain your read-only keys, the other will be a regular HashMap.
When you get a key, look in both maps, beginning by the unmodifiable map. When you put, use only the second map, after checking that the key is not already in the first map.

How to make a CaseInsensitiveConcurrentMap?

How can I implement
class CaseInsensitiveConcurrentMap<V> implements ConcurrentMap<String , V>
which works just like ConcurrentHashMap<String , V> except that the keys are compared case-insensitively? The keys should not be converted to lowercase or uppercase.
Note that Collections.synchronizedMap(new TreeMap<String, new MyCaseInsensitiveComparator()) is no solution as it allows no concurrency and misses the additional methods.
Creating a String-like class with case-insensitive equals and hashCode is no option either, since the map has to be passed to methods expecting strings as keys.
Have you tried
ConcurrentMap<String, Object> map =
new ConcurrentSkipListMap<String, Object>(
String.CASE_INSENSITIVE_ORDER);
If you really insist on all your constraints (ConcurrentHashMap) and String keys:
Overwrite any ConcurrentHashMap method and convert any key with key = key.toLowerCase().
Looks like a lot of work, and a lot of unit testing, so if I were you I would try to discuss the interface.
Example:
#Overwrite
public void V get(String key) {
return super.get(transform(key));
}
// Extracted that to avoid cluttering your whole code with that specific transformation
// So you can easily change it
private String transform(String key) {
return key.toLowerCase();
}
Ugly, but, given your constraints, feasible.

Categories