I have a piece of code that loops through a list and builds up a map containing a date as a key and a value of Pair<BigDecimal, Currency>>. It loops though for every currency. It builds up the map correctly the first loop around but every loop after replaces the data due to sharing a date and therefore a key.
How could I change the logic below to handle building a map sharing a date to prevent different currency's amounts being added together?
Consider using a Multimap from the Guava library.
It will be like a Map<Date, List<Pair>>.
Here is the description from the javadoc:
A collection that maps keys to values, similar to Map, but in which each key may be associated with multiple values. You can visualize the contents of a multimap either as a map from keys to nonempty collections of values:
Related
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));
}
I would like to insert a key-value pair into a Map only if the key is not already present, otherwise I would like to retrieve the existing value without replacing it. I can do this with a get(), followed by a conditional put(). However this requires resolving the location of the key in the Map twice (2 hashes, 2 tree traversals, etc depending on the map implementation) which seems unnecessary. Do any of the standard Map implementations allow this operation to be condensed, similar to how map::insert works in the C++ STL?
ConcurrentMap has putIfAbsent method.
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ConcurrentHashMap.html
I'm looking for a way to have a concurrent map or similar key->value storage that can be sorted by value and not by key.
So far I was looking at ConcurrentSkipListMap but I couldn't find a way to sort it by value (using Comparator), since compare method receives only the keys as parameters.
The map has keys as String and values as Integer. What I'm looking is a way to retrieve the key with the smallest value(integer).
I was also thinking about using 2 maps, and create a separate map with Integer keys and String values and in this way I will have a sorted map by integer as I wanted, however there can be more than one integers with the same value, which could lead me into more problems.
Example
"user1"=>3
"user2"=>1
"user3"=>3
sorted list:
"user2"=>1
"user1"=>3
"user3"=>3
Is there a way to do this or are any 3rd party libraries that can do this?
Thanks
To sort by value where you can have multiple "value" to "key" mapping, you need a MultiMap. This needs to be synchronized as there is no concurrent version.
This doesn't meant the performance will be poor as that depends on how often you call this data structure. e.g. it could add up to 1 micro-second.
I recently had to do this and ended up using a ConcurrentSkipListMap where the keys contain a string and an integer. I ended up using the answer proposed below. The core insight is that you can structure your code to allow for a duplicate of a key with a different value before removing the previous one.
Atomic way to reorder keys in a ConcurrentSkipListMap / ConcurrentSkipListSet?
The problem was to keep a dynamic set of strings which were associated with integers that could change concurrently from different threads, described below. It sounds very similar to what you wanted to do.
Is there an embeddable Java alternative to Redis?
Here's the code for my implementation:
https://github.com/HarvardEconCS/TurkServer/blob/master/turkserver/src/main/java/edu/harvard/econcs/turkserver/util/UserItemMatcher.java
The principle of a ConcurrentMap is that it can be accessed concurrently - if you want it sorted at any time, performance will suffer significantly as that map would need to be fully synchronized (like a hashtable), resulting in poor throughput.
So I think your best bet is to return a sorted view of your map by putting all elements in an unmodifiable TreeMap for example (although sorting a TreeMap by values needs a bit of tweaking).
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.
I need a collection that behaves something like C++ multimap, but I also need to be able to get elements by a range of keys.
You can look into Google Collections. It has multiple implementations for MultiMap.
There is no built-in multimap collection in Java. To solve this you can map to every key a list of values: Map<String, List<String>>, for example. Otherwise there are third-party libraries with implemented multimaps - here is one of them.
There is a simple hack around creating multimap sortable collections in java...Use the dataset TreeMap and for keys enter key*10^4+counter. This way you are storing duplicate key values in the map (by adding counter they are actually not duplicates, so you can store the in treeMap, but you know not to use the last four digits of the integer key values), however your dataset is being sorted using your original key values. Note that depending how large is your dataset you might want to adjust 10^n to make sure that it is larger then the number of entries in your data.