Merge maps in java - java

What i'm trying to do is pretty simple.I want to merge two maps.
Say
map1={(1,"one"),(2,"two"),(3,"three");
map2={(1,"onetoo"),(4,"four")};
if i follow this->
map3.putall(map1);
map3.putall(map2);
then value of 1 is onetoo but when i follow the reverse it is one.
is there anyway i could change that?what i mean is that java overwrites and puts only the latest value for a key.
i.e if onetoo was added after one (in their respective maps)then no matter what the order of putall calls to map3 the value remains onetoo.

There is no way to do that, unless you store the actual time when the values were added.
Say map1={(1,("one", 15:15)), (2, ("two", 15:16))}
Then you can add all of map1 and then iterate over map2 adding only if the key is not already there or if it's there but with a earlier timestamp.

That's how maps work, they use the hashcode of the object you set as key as a way to identify its self within the map entries, and as you can see it has to be unique.
So you would have to specify another key since an integer value of 1 has a hashcode of 1.

Well, If your programe worked as you want, then predict the output of the following
map3.get(1);
You could never know whether it is "One" or "Onetoo".
Too prevent any such problem, Maps in Java are designed to contain only unique keys.
So , if you write
map3.putall(map1);
the value of 1 is "one". but as soon as you write
map3.putall(map2);
the value of 1 is reset and it becomes "onetoo". reverse happens when you reverse it. Possible solutions could be.
Put in your keys in maps in such a way that they (keys) uniquely identifies an object. So that whenever in future you merge maps, no clash happens(in terms of duplicity) in keys.
If you can't do it, then a possible solution could be to get all keys of every map and check for duplicity and change the duplicate keys in such a way that you can retrieve your objects without hassle.

Related

Is there an efficient way of checking if HashMap contains keys that map to the same value?

I basically need to know if my HashMap has different keys that map to the same value. I was wondering if there is a way other than checking each keys value against all other values in the map.
Update:
Just some more information that will hopefully clarify what I'm trying to accomplish. Consider a String "azza". Say that I'm iterating over this String and storing each character as a key, and it's corresponding value is some other String. Let's say I eventually get to the last occurrence of 'a' and the value is already be in the map.This would be fine if the key corresponding with the value that is already in the map is also 'a'. My issue occurs when 'a' and 'z' both map to the same value. Only if different keys map to the same value.
Sure, the fastest to both code and execute is:
boolean hasDupeValues = new HashSet<>(map.values()).size() != map.size();
which executes in O(n) time.
Sets don't allow duplicates, so the set will be smaller than the values list if there are dupes.
Very similar to EJP's and Bohemian's answer above but with streams:
boolean hasDupeValues = map.values().stream().distinct().count() != map.size();
You could create a HashMap that maps values to lists of keys. This would take more space and require (slightly) more complex code, but with the benefit of greatly higher efficiency (amortized O(1) vs. O(n) for the method of just looping all values).
For example, say you currently have HashMap<Key, Value> map1, and you want to know which keys have the same value. You create another map, HashMap<Value, List<Key>> map2.
Then you just modify map1 and map2 together.
map1.put(key, value);
if(!map2.containsKey(value)) {
map2.put(value, new ArrayList<Key>);
}
map2.get(value).add(key);
Then to get all keys that map to value, you just do map2.get(value).
If you need to put/remove in many different places, to make sure that you don't forget to use map2 you could create your own data structure (i.e. a separate class) that contains 2 maps and implement put/remove/get/etc. for that.
Edit: I may have misunderstood the question. If you don't need an actual list of keys, just a simple "yes/no" answer to "does the map already contain this value?", and you want something better than O(n), you could keep a separate HashMap<Value, Integer> that simply counts up how many times the value occurs in the map. This would take considerably less space than a map of lists.
You can check whether a map contains a value already by calling map.values().contains(value). This is not as efficient as looking up a key in the map, but still, it's O(n), and you don't need to create a new set just in order to count its elements.
However, what you seem to need is a BiMap. There is no such thing in the Java standard library, but you can build one relatively easily by using two HashMaps: one which maps keys to values and one which maps values to keys. Every time you map a key to a value, you can then check in amortized O(1) whether the value already is mapped to, and if it isn't, map the key to the value in the one map and the value to the key in the other.
If it is an option to create a new dependency for your project, some third-party libraries contain ready-made bimaps, such as Guava (BiMap) and Apache Commons (BidiMap).
You could iterate over the keys and save the current value in the Set.
But, before inserting that value in a Set, check if the Set already contains that value.
If this is true, it means that a previous key already contains the same value.
Map<Integer, String> map = new HashMap<>();
Set<String> values = new HashSet<>();
Set<Integter> keysWithSameValue = new HashSet<>();
for(Integer key : map.keySet()) {
if(values.contains(map.get(key))) {
keysWithSameValue.add(key);
}
values.add(map.get(key));
}

HashMap.put() is overriding existing item for different key

I have a HashMap which I am using to store objects of type SplitCriteria using a String as the key
Map<String, SplitCriteria> criteriaMap = new HashMap<String, SplitCriteria>();
A sample SplitCriteria object contains the something like the following:
SplitCriteria [
id=4,
criteriaName="Location",
criteriaAbrevName="Loc",
fieldName="LOCATION",
isMandatory=false
]
with id being a long, isMandatory is a boolean and the rest are strings.
I am looping over previously populated Array of the same object type, total count is 7, adding each to the HashMap using the fieldName attribute as the key:
for(SplitCriteria split : selectedCriteria){
String fieldName = split.getFieldName();
criteriaMap.put(fieldName, split);
}
After this loop has finished, the size of the map appears to be 7, but looking at the table contents there are only 6 objects present.
From researching the issue, I have come to understand that if there is a clash with keys, the clashing objects are "chained" together using the next attribute of the entry in the Map.
From the image below, you can see this is what is happening in my scenario, but the two keys are completely different!
Also I read this in the docs for the put method
If the map previously contained a mapping for the key, the old value is replaced by the specified value
and
Returns:
the previous value associated with key, or null if there was no mapping for key.
So if the keys were clashing, I would expect the old entry to be returned, but it is not.
I have no clue how this is happening, as each key I am using is completely different to the next.
Any help in resolving this would be greatly appreciated.
Paddy
EDIT:
When I try and retrieve the object at a later stage I am getting a null reponse
SplitCriteria criteria = (SplitCriteria) criteriaMap.get(key);
but looking at the table contents there are only 6 objects present
Nope, look at size - it's 7. You've just got two values in the same bucket. They don't collide by exact hash value, but they do collide by bucket. That's fine.
You won't be able to observe that when you use the map - if you just use the public API, you'll see all 7 entries with no hint of anything untoward. This is why I would generally recommend avoiding digging into the internal details of an object using the debugger until you're really really sure there's a problem.
HashMap is organized into buckets.
Every bucket has a linked list with entries for that bucket.
In your case, you have sixteen buckets (the size of table), six of them are filled (objects in table), and your seven entries are in those six lists (which means that one of them has length two).
If you open those HashMap$Entry objects, you will find one that has a pointer to the "next" entry.
"LOCATION" and "PAY_FREQUENCY" happen to be in the same bucket.
If you continue to shove more entries into the map, it will eventually resize itself to have more buckets (to avoid running into issues with long lists).
Two different keys may be assigned to the same bin of the HashMap (the same array entry in Java 6 implementation). In that case they will be chained in a linked list. However, neither of these two keys overrides the other, since they are not equal to each other.
The size of your HashMap is 7, which means it contains 7 key-value pairs (even though 2 of them are stored in the same bin).
A clash happens when two different keys produce the same hash value. This hashed value is used in the HashMap to quickly navigate to the elements. So this means, that when two keys clash, they are different but both produce the same hash value. The algorithm that is used to calculate the hash value is internal to the HashMap.
Take a look at this blog post: http://javahungry.blogspot.com/2013/08/hashing-how-hash-map-works-in-java-or.html
The table only has 16 entries. This means that keys are assigned to buckets only based on 4 bits, so two entries in the same bucket isn't that unlikely at all. The table will grow as you add more entries.
You don't need to care about these details. All you should care about is that the map has 7 entries.
Since hashcode is same for both the keys, bucket location would be same and collision will occur in HashMap, Since HashMap use LinkedList to store object, this entry (object of Map.Entry comprise key and value ) will be stored in LinkedList.
HashMap uses Key Object's hashcode to find out bucket location and retrieves Value object ,then there are two Value objects are stored in same bucket . HashMap stores both Key and Value in LinkedList node .
After finding bucket location , we will call keys.equals() method to identify correct node in LinkedList and return associated value object for that key in Java HashMap

ConcurrentSkipListMap put in order

I have a ConcurrentSkipListMap of keys and values. It is very important to hold the order of the keys.
The problem appears when I try to insert a new value in a particular position. The only one method to insert a value is the put() that put this value in the last position.
With the replace method it can only edit the value, not the key.
Is it possible? What can I do? Can you tell me another class to do it?
The ConcurrentSkipListMap holds the order of the keys on its own as they are sorted.
So either you were not aware of it or you actually do not want to hold order of the keys but manipulate them yourself.
If you meant that you want the keys returned in the order in which they were put into the map than use the separet List (ConcurrentQueue if you need concurrency) and place the keys there manually.

Should custom key objects be immutable ?If yes , then why?

Okay I want to have custom user defined objects as keys in my HashMap instead of say String. Should the candidate objects be immutable ? I read somewhere that the best practice is to make them immutable but I can not figure out the reason myself .
If you have a mutable key in a HashMap, then it will end up in the wrong bucket, which totally breaks the Map.
insert key, hashCode() is called, bucket assigned
change key, hashCode changes, no longer matches the bucket
look up by (new) key, hashCode() leads to the wrong bucket, value not found
look up by (old) key, hashCode() leads to the "correct" bucket, but now the key found there is no longer equal (because it is the "new" key now), so it is also discarded
If you have a mutable key in a TreeMap, then it will end up in the wrong position of the tree, which is supposed to be sorted (and that happens at insertion-time). Basically same flow as above.
And since we like similes around here, this is like changing your name in an existing phonebook with a magic marker without printing a whole new book: So your new name "Smith" will still be listed between "John" and "Johnston" (where no one will look for it), and no one will find it between "Smart" and "Smithers" (where they are looking for it). TreeMap works just like a phonebook.
Yes they should be immutable as they would not function well as keys if they could be changed. Imagine buying a lock and key for your house, but then deciding you'd like to make the key prettier by hammering it into a different shape. It wouldn't work very well, would it? The same principles apply here.
Yes. If you update key from somewhere else then you can no longer lookup the value stored for that key.

Can i have HashSets as the keys in a HashMap? Suggest an alternative if not

Edit: explained the problem properly now.
I have a hashmap where i want to store sets of words seen together (key) and the lines in which they were seen together(value). This is the structure i came up with:
HashMap<HashSet<String>, HashSet<Integer>> hm= ...
for inputs:
mango, banana, apple
apple, banana
peach, walrus
walrus, peach
As I read this, line by line, I make new temporary keys (hashsets not yet inserted into hashmap) from the combination of words in the line. Each temporary key is a hashset of a subset of the words in the line. If a temporary key already exists in my hashmap, which i check by
if(hashmap.containsKey(hashset))
i simply add the new line to that key's corresponding value, if not, I make a new entry in the hashmap and take care of it.
At no point do i change an existing key. I only update their corresponding values in the hasmmap.
my hashmap, at the end of reading the file, should look something like this
[apple, banana]=[1,2]
[peach, walrus]=[3,4]
...
the problem is that the
if(hashmap.containsKey(hashset))
piece of code doesn't always detect existing keys. Why is this? Is this structure not allowed?
Thank you
This should work, but you need to watch out for mutability of the keys. If you ever change the contents of one of the keys, its hashcode will change, and your map will start doing strange things. From the javadoc for Map:
Note: great care must be exercised if mutable objects are used as map
keys. The behavior of a map is not specified if the value of an object
is changed in a manner that affects equals comparisons while the
object is a key in the map. A special case of this prohibition is that
it is not permissible for a map to contain itself as a key. While it
is permissible for a map to contain itself as a value, extreme caution
is advised: the equals and hashCode methods are no longer well defined
on such a map.
To avoid this, wrap the keys with Collections.unmodifiableSet() immediately upon creation, or just use ImmutableSet from Guava.
You can, but once you have added a HashSet as a key to a HashMap you shouldn't modify it again, as the HashSet.hashCode() might change and you'll never find your HashSet again. In other words, if you're doing something like that, be sure that your keys are immutable HashSets (see also Matt's answer here)
An alternative is to use the MultiKeyMap along with a MultiKey from commons collections
The problem you have is well explained by #Lukas ans #Matt.
I think you could get away by using extending or using a decorator pattern to create a Hashset that overides equals and hashCode in a way that is independent of the contents.
This way you can avoid introducing dependencies on third party jars just for a specific problem

Categories

Resources