I am using a concurrent hashmap of structure
Map<Set<Date>, Map<String, Object>> SampleMap
The Map used inside the given map (Map<String, Object>) is also a concurrent hashmap,
but set is a only TreeSet type.
Still I get concurrent Modification exception when I add following line in logs,
logger.debug("sampleMap.keySet() + ". Size is " + sampleMap.keySet().size()");
and also in some other parts of same class dealing with this map.
This map is extensively used in Batch process by multiple threads to put and remove values in map and java version used is 1.5.
I think the exception is due to Treeset and also i find there is no similar implementation of concurrent handling collection for type Set.
It would be great if any one confirm whether my thinking over given issue is correct and also please suggest solution for this problem?
Since you need to be able to "modify" the key, you need to follow this pattern
// lock the collection
Map<String, Object> values = map.remove(key);
key = new TreeSet<String>(key);
// modify copy of key
map.put(key, values);
// unlock the collection.
As you are performing an operation which ConcurrentMap does not support, you have to use your own locking. You can use a plain HashMap or LinkedHashMap with synchronized or ReentrantReadWriteLock.
You can create a Concurrent set using
// Added in Java 1.6
Set<String> set = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
// or to be sorted
Set<String> set = Collections.newSetFromMap(new ConcurrentSkipListMap<String, Boolean>());
However, you can't change the contents of a key so what you should be using is
Set<String> key = Collections.unmodifiableSet(treeSet);
// or to be sure its not modified
Set<String> key = Collections.unmodifiableSet(new TreeSet<String>(treeSet));
A simple example of why you cannot change a key after using it in a Map.
Set<String> key1 = new TreeSet<String>();
Map<Set<String>, Boolean> map = new ConcurrentHashMap<Set<String>, Boolean>();
map.put(key1, true);
System.out.println("Is the map ok? "+map.containsKey(key1));
key1.add("hello");
System.out.println("Is the map ok? "+map.containsKey(key1));
prints
Is the map ok? true
Is the map ok? false
The common behaviour is that it can no longer see the key in the map. This is because the map places the key into a bucket based on its hashCode. If the hashCode changes, it can be in the wrong bucket so when it looks for it, it can't find it.
Related
below is my code...
Map<Integer, String> MyType = sessionInfo.getType();
//{2=somename}
I am trying to get key from value...without running any loops....is it possible?
MyType.get("somename") // should output 2`
It's not easy to get key from value in Hashtable or HashMap, as compared to getting value from key, because Hash Map or Hashtable doesn't enforce one to one mapping between key and value inside Map in Java. infact Map allows same value to be mapped against multiple keys inside HashMap, Hashtable or any other Map implementation.
String key= null;
String value="somename";
for(Map.Entry entry: MyType.entrySet()){
if(value.equals(entry.getValue())){
key = entry.getKey();
break; //breaking because its one to one map
}
}
I would encourage running a loop for simplicity. It most likely will not slow down your program a noticeable amount.
However, if you must not run a loop, Google's Guava library has a BiDirectional Map Collection called BiMap that can be (found here). The map works both ways and is guaranteed to be synchronized at all times. I also am assuming that you have unique values in your map. If you do not, duplicate values will not have a specific key to link to.
BiMap<String, Integer> biMapInversed = biMap.inverse(); // how to get inverted map
Again, I wouldn't encourage this unless absolutely necessary. Looping through will work perfectly fine in most cases.
Taken from this SO answer
If you choose to use the Commons Collections library instead of
the standard Java Collections API, you can achieve this with ease.
The BidiMap interface in the Collections library is a
bi-directional map, allowing you to map a key to a value (like normal
maps), and also to map a value to a key, thus allowing you to perform
lookups in both directions. Obtaining a key for a value is supported
by the getKey() method.
There is a caveat though, bidi maps cannot have multiple values mapped
to keys, and hence unless your data set has 1:1 mappings between keys
and values, you cannot use bidimaps.
This is not possible. You need to consider the value may be duplicated in map.
Ex, How do you deal with {2=somename} and {5=somename}
You still need to use a for loop to check value and get key and decide to break or go on when value is matched.
If you're sure that your values are unique you can iterate over the entries of your old map .
Map<String, Character> myNewHashMap = new HashMap<>();
for(Map.Entry<Character, String> entry : myHashMap.entrySet()){
myNewHashMap.put(entry.getValue(), entry.getKey());
}
Alternatively, you can use a Bi-Directional map like Guava provides and use the inverse() method :
BiMap<Character, String> myBiMap = HashBiMap.create();
myBiMap.put('a', "test one");
myBiMap.put('b', "test two");
BiMap<String, Character> myBiMapInversed = myBiMap.inverse();
I am currently working on Data Structures for writing a program on encryption and decryption of names. I have a doubt in Map interface. Actually to get the value associated with a key we have get() method in Map interface. But how to retrieve the key of a particular value without iterating through all the key value pairs in Map interface
Thank you
As others have said, it can't be done. The Map interface and its implementations do not support that.
Consider using a BiMap such as the one inculed in Google Guava Collections. It establishes a one-to-one (bidirectional) relationship between keys and values.
https://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#BiMap
Using BiMap you can use Key key = biMap.inverse().get(value) to get a key for a given value.
Given that values are unique, you could to it like this:
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
String key = map.entrySet().stream().
collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey))
.get("value1");
System.out.println(key); //gives key1
how to retrieve the key of a particular value without iterating through all the key value pairs in Map interface
Key is Key, not the value. You cannot do it. That's not how Map implemented.
Even If you make it with some magic (iterating multiple times, checking for equls etc ..), that's not guaranteed to give expected result..
And as per the definition of Map, Key is unique not the value. So there will be duplicated values and when you get by value, which associated key you will expect to get ?
If you are sure that there are no duplicates, you can do
for (Entry<Integer, String> entry : testMap.entrySet()) {
if (entry.getValue().equals("c")) {
System.out.println(entry.getKey());
}
}
Well like everyone said, you can't really do it in a decent way because there can be duplicate values. You could search for a hit using the equals() method and compare values. but then again, why are you even using a key/value map if you wanted to do such a thing.
in short, you could write your own version of the get method, taking in a instance of the value object you are trying to get. But there really is not a point in using a map if you are going to do that.
Why not use a list of some sorts of you desire to search on value ?
You can not do that because the "values" can be duplicated.
As said, that is not provided by Java-Map interface, since you should have a key and get the value then.
Usually you have something like this.
User user = ...;
HashMap<String, User> usernamesToUser = ...
Then you can get the key by something like:
String username = user.getUsername();
So without using the map actually.
However what you can do is, if the key is not directly to retrieve from the object you can use two Maps for both directions. So consider former example (just assume User the User object does not safe the Username)
Map<User, String> userMapReverse = ....;
Map<String, User> userMap = ....;
String username = userMapReverse.get(user);
However this option requires that you maintain two maps, which can be pretty ugly sometimes.
Is there any way to replace a key using put() in a LinkedHashMap without losing the order that the key was originally inserted in?
You do not lose the order when putting a different value for the same key.
Example
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("foo", "bar");
map.put("blah", "baz");
System.out.println(map);
map.put("foo", "foo");
System.out.println(map);
Output
{foo=bar, blah=baz}
{foo=foo, blah=baz}
Edit
"Replacing" a key for a given value would imply removing the key value pair, then putting the new key with the stored value.
As such, there is no direct way to do this with a LinkedHashMap, probably not even by inheriting and changing the behavior of remove and put.
If you used the LinkedHashMap, I don't think there is built-in method to achieve your goal. You may want to pick another (or design your own) data-structure.
If you have to do it on a linkedhashmap, you can create a new LinkedHashMap, iterate the old one and put into the new one, when your target entry comes, create a new entry with different key, put the new entry into the map.
How can one perform set operations on a HashMap, such as Collections.addAll()?
Based on you comments to the questions asked I think what you really need is a Set not a Map.
Try
Set<String> mySet = new HashSet<String>();
mySet.addAll(...);
Use mySet.contains("someString"); for quick determination if a value exists. It should be equivalent of what you seem to be trying to do.
Through for instance Map.putAll.
You may also be able to do set-operations directly on the set of map entries which you can get hold of through Map.entrySet.
From the documentation:
Returns a Set view of the mappings contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa.
This way:
hashMap.putAll(map);
From documentation:
Copies all of the mappings from the
specified map to this map These
mappings will replace any mappings
that this map had for any of the keys
currently in the specified map.
You can do operations like
Map<String, String> map = new HashMap<String, String>();
Set<String> set = map.keySet();
for(String s: set);
set.retainAll(set2); // keeps the keys in set2
set.removeAll(set3); // removes the keys in set3
set.remove(s);
You can also turn a Map in a Set. There is no ConcurrentHashSet but you can do
Set<String> set = Collections.setFromMap(new ConcurrentHashMap<String, Boolean>());
I have a Set of keys and a List of key/value pairs. The values are of the form Long,BigInteger.
// key/values pairs: Long,BigInteger
List<Object[]> values;
// id list that corresponds to the keys for the list above
Set<Long> ids;
If any member of the key Set does not exist as a key in the key/value list, I want to add it to the list with a value of 0.
What's a good way to do this in Java?
The various commenters suggesting maps make a good point. How about instead of
List<Object[]> values
you use
Map<Long, BigInteger> values
In that case:
for(Long id : ids) {
if(!values.containsKey(id)) {
values.put(id, BigInteger.ZERO);
}
}
In fact, even if the code as must be kept as written I'd consider using a map for manipulation by pre-processing the list into a map, then dumping it back into the list of object arrays.
What's a good way to do this in Java?
Replace the Set<Long> and List<Object[]> by a Map<Long, BigInteger>. If the ordering is not important, then use HashMap. If you'd like to sort automagically on keys, then use TreeMap. If you'd like to maintain insertion order, then use LinkedHashMap.
E.g.
Map<Long, BigInteger> unorderedMap = new HashMap<Long, BigInteger>();
Map<Long, BigInteger> orderedByKeys = new TreeMap<Long, BigInteger>();
Map<Long, BigInteger> orderedByInsertion = new LinkedHashMap<Long, BigInteger>();
This way you can just use any of the Map methods to handle key/value pairs. E.g.
Long key = 1L;
BigInteger value = map.get(key);
if (value == null) {
value = new BigInteger(0);
map.put(key, value);
}
You can even get all keys by Map#keySet():
Set<Long> keys = map.keySet();
To learn more about maps, consult Sun's own tutorial about the subject.
I think you want to use something like one of the Google Collections Multimap implementations. Don't re-invent the wheel. The Apache Commons has something similar I suspect, but I prefer the Google library.
Querying for a key that has no values returns an empty collection.
EDIT: options for sorting order, uniqueness, etc are all available, just pick the right implementation according to your requirements.