public class MapCheck {
public static void main(String[] args) {
Map<String, String> data = new HashMap<String, String>();
data.put("John", "Taxi Driver");
data.put("Mark", "Professional Killer");
Map<String, String> data1 = new HashMap<String, String>();
data1.put("John", "Driver");
data1.put("Mark", "Murderer");
Map<String, String> data3 = new HashMap<String, String>();
data3.putAll(data);
data3.putAll(data1);
System.out.println(data3);
}
}
I have few maps which contains same key, their values are different. I want to merge them. But when I merge them with the usual putAll() it gives me only the value of the key which was inserted latest.
Output from above code is {John=Driver, Mark=Murderer}
Is there a method which will get me all the values associated with the key and give me as a array like
{John=[Taxi Driver, Driver], Mark=[Professional Killer, Murderer]}
You can produce a Map<String, List<String>> quite easily with Java 8 Streams:
Map<String, List<String>>
merged =
Stream.of(data,data1) // create a Stream<Map<String,String> of all Maps
.flatMap(map->map.entrySet().stream()) // map all the entries of all the
// Maps into a
// Stream<Map.Entry<String,String>>
.collect(Collectors.groupingBy(Map.Entry::getKey, // group entries by key
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())));
The output Map:
{John=[Taxi Driver, Driver], Mark=[Professional Killer, Murderer]}
Something like this?
void add(String key, String value, Map<String,List<String>> map) {
List<String> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(value);
}
the data structure Multimap may be the best choice. The implementation in guava is highly recommended.
https://github.com/google/guava/wiki/NewCollectionTypesExplained#multimap
You may want to create Map<String, <ArrayList<String>>. That way you can store multiple values that are associated with one key.
You may consider creating method that provides storing multiple values associated to unique key in specified map:
public static void add(String key, String value, Map<String, List<String>> map) {
// Here you create a refeREnce to instance of the list storing values associated with given key:
List<String> listOfValues = map.get(key);
// If given key was already present in the map...
if(listOfValues != null) {
// Add given value to list of values associated with given key:
listOfValues.add(value);
// If given key wasn't present in the map...
} else {
// Initialize an ArrayList that will store given value in the map:
listOfValues = new ArrayList<>();
// Put new entry into the given map (given key and to-be-filled list of values:
map.put(key, listOfValues);
// Put given value to the list of values associated with given key
listOfValues.add(value);
}
}
Related
I have a multimap like below:
{20014=[13123], 20013=[45451, 13123]}
where the keys and values are in String
If there is any duplicate in the value from other key, I have to print that key-value pair. In this case, it will be Key-20013,Value-13123.
How to achieve this?
I checked this link but not getting how to get the duplicate pair.
It could be done like this:
// Initialize my multimap
Multimap<String, String> multimap = ArrayListMultimap.create();
multimap.put("20014", "13123");
multimap.put("20013", "45451");
multimap.put("20013", "13123");
// Set in which we store the values to know if they exist already
Set<String> allValues = new HashSet<>();
// Convert the multimap into a Map
Map<String, Collection<String>> map = multimap.asMap();
// Iterate over the existing entries
for (Map.Entry<String, Collection<String>> entry : map.entrySet()) {
String key = entry.getKey();
Collection<String> values = entry.getValue();
// Iterate over the existing values for a given key
for (String value : values) {
// Check if the value has already been defined if so print a log message
if (!allValues.add(value)) {
System.out.println(String.format("Key-%s,Value-%s", key, value));
}
}
}
Output:
Key-20013,Value-13123
You can invert your multimap and, viewed as a map, iterate through its entries:
Multimap<String, String> inverse = Multimaps.invertFrom(multimap, HashMultimap.create());
for (Map.Entry<String, Collection<String>> entry : inverse.asMap().entrySet()) {
String value = entry.getKey();
Iterator<String> keysIterator = entry.getValue().iterator();
assert keysIterator.hasNext() : "there is always at least one key";
keysIterator.next(); // skip first key
while (keysIterator.hasNext()) { // each additional key is a duplicate
String key = keysIterator.next();
System.out.println(String.format("Key-%s,Value-%s", key, value));
}
}
Output:
Key-20013,Value-13123
If you are using an ImmutableMultimap then instead of Multimaps.invertFrom(Multimap, M) you can simply use ImmutableMultimap.inverse():
ImmutableMultimap<String, String> inverse = multimap.inverse();
If you simply want a map of duplicated values to their respective keys then you can use Maps.filterValues(Map, Predicate):
Map<String, Collection<String>> keysByDuplicatedValue = Maps.filterValues(inverse.asMap(),
keys -> keys.size() > 1);
Which will give you a map like below:
{13123=[20014, 20013]}
I have a populated hashmap in the form Hashmap<String , ArrayList<Double>>. There are many cases in which the values (Arraylist) are the same. What I want tro do is if the value is same , the two separate entries should be changed to one with a change in the key. For eg:
HashMap<String, ArrayList<Double>> map = new HashMap<>();
ArrayList<Double> arr = new ArrayList<>();
arr.add(1);
arr.add(2)
map.put("a",arr);
map.put("b",arr);
map.put("c",arr);
This should result in a hashmap with a single entry :
{a, b , c=[1,2]}
I have tried to solve this for hours but am still unable to do so. A million thanks in advance.
Creating your custom HashMap by overriding put() should work, performance-wise it is not really efficient though.
Something like this:
public class CustomMap extends HashMap<String, ArrayList<Double>> {
#Override
public ArrayList<Double> put(String key, ArrayList<Double> value) {
for (Entry<String, ArrayList<Double>> entry : entrySet()) {
if (entry.getValue().equals(value)) {
String oldKey = entry.getKey();
remove(oldKey);
String newKey = oldKey + ", " + key;
return super.put(newKey, value);
}
}
return super.put(key, value);
}
}
To use it, just replace the following:
HashMap<String, ArrayList<Double>> map = new HashMap<>();
With:
CustomMap map = new CustomMap();
Maps relate keys with values. So even if the values are the same, you can't combine these entries because the keys are different
I have created a map called result.
In the sortByKeys method as my keys are String with Numeric values, I have converted them to Integer key type Map then sorted them.
The sorting is working fine when I am looping and printing individually, but not when I am setting them in another Map.
public class TestDate {
public static void main (String args[]){
Map<String, String> result = new HashMap<String, String>();
result.put("error", "10");
result.put("1","hii");
result.put("Update","herii");
result.put("insert","insert");
result.put("10","hiiuu");
result.put("7","hii");
result.put("21","hii");
result.put("15","hii");
Map<String, String> sorted = sortByKeys(result);
//System.out.println(sorted);
}
private static Map<String, String> sortByKeys(Map<String, String> map) {
Map <Integer,String> unSorted = new HashMap<Integer, String>();
Map <String,String> sorted = new HashMap<String, String>();
for (Map.Entry<String, String> entry : map.entrySet())
{
try{
int foo = Integer.parseInt(entry.getKey());
unSorted.put(foo, entry.getValue());
}catch (Exception e){
}
}
Map<Integer, String> newMap = new TreeMap<Integer, String>(unSorted);
Set set = newMap.entrySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()) {
Map.Entry me = (Map.Entry)iterator.next();
System.out.println(me.getKey());
System.out.println(me.getValue());
sorted.put(me.getKey().toString(), me.getValue().toString());
}
System.out.println(sorted);
return null;
}
}
Here is the o/p :
1
hii
7
hii
10
hiiuu
15
hii
21
hii
{21=hii, 10=hiiuu, 1=hii, 7=hii, 15=hii}
If you don't need the last inch of performance, you can solve this rather directly, without an extra step to sort the map, by using SortedMap:
Map<String,String> result = new TreeMap<>(Comparator.comparingInt(Integer::parseInt));
If you are among the unfortunate bunch who are still being denied access to Java 8, you'll have to implement the Comparator in long-hand:
new TreeMap<>(new Comparator<String,String> { public int compare(String a, String b) {
return Integer.compare(Integer.parseInt(a), Integer.parseInt(b));
}});
The above approach works only under the assumption that all keys are parseable integers. If that is not the case, then you won't be able to use the SortedMap directly, but transform your original map into it, filtering out the unparseable keys.
It's because the Map you're putting them into is a HashMap, which isn't sorted. There's no guarantee of the ordering of results you'll get out of the HashMap, even if you put them in in the right order.
(And calling it sorted doesn't change anything :) )
You print 2 different maps and not the same: you iterate over and print the entries of newMap map, and at the end you print sorted map.
You see the sorted entries printed because you iterate over your sorted newMap.
Then you print the sorted map which is unsorted (despite by its name). You print a different map instance.
Print this:
System.out.println(newMap); // This is the instance of the sorted "TreeMap"
In this problem, I have to find all matching key/value mappings within two maps and then return it into a new map, but I am running into some problems. My idea is to find all matching keys from the two maps, then use these keys to reference it to the values. If the values matched, I would put the key/value into the map. I am trying to find out why it just adds all the keys that are in common; it only add those keys if its corresponding values match too. Thanks.
The prompt:
Write a method intersect that takes two Maps of strings to integers as parameters and that returns a new map whose contents are the intersection of the two. The intersection of two maps is defined here as the set of keys and values that exist in both maps. So if some key K maps to value V in both the first and second map, include it in your result. If K does not exist as a key in both maps, or if K does not map to the same value V in both maps, exclude that pair from your result. For example, consider the following two maps:
{Janet=87, Logan=62, Whitaker=46, Alyssa=100, Stefanie=80, Jeff=88, Kim=52, Sylvia=95}
{Logan=62, Kim=52, Whitaker=52, Jeff=88, Stefanie=80, Brian=60, Lisa=83, Sylvia=87}
Calling your method on the preceding maps would return the following new map (the order of the key/value pairs does not matter):
{Logan=62, Stefanie=80, Jeff=88, Kim=52}
My code:
// we need to store the keys, then get the values in common, then put the key/map into map
public static Map<String, Integer> intersect(Map<String, Integer> first, Map<String, Integer> second) {
Map<String, Integer> output = new HashMap<String, Integer>(first); // combined output
Set<String> keyFirst = new HashSet<String>(); // stores all the keys for first
for (String key: first.keySet()) { // goes through each key in input
keyFirst.add(key); // adds all keys from first into keyFirst
}
// goes through each key in common and checks to see if they reference to the same value
Iterator<String> keyFirstItr = keyFirst.iterator();
while (keyFirstItr.hasNext()) {
String keyTemp = keyFirstItr.next();
if (first.get(keyTemp) == second.get(keyTemp)) { // If same key, same value mapped
output.put(keyTemp, first.get(keyTemp)); // add key value to map
}
}
return output;
}
You are putting all the values from first to output by passing it to the constructor.
Map<String, Integer> output = new HashMap<String, Integer>(first); // you are passing first to the constructor.
You don't need to create another Set, keySet() method returns set so the below lines not required.
Set<String> keyFirst = new HashSet<String>(); // stores all the keys for first
for (String key: first.keySet()) { // goes through each key in input
keyFirst.add(key); // adds all keys from first into keyFirst
}
Here's the correct implemetataion.
// we need to store the keys, then get the values in common, then put the key/map into map
public static Map<String, Integer> intersect(Map<String, Integer> first, Map<String, Integer> second) {
Map<String, Integer> output = new HashMap<String, Integer>(); // combined output
// goes through each key in common and checks to see if they reference to the same value
Iterator<String> keyFirstItr = first.keySet().iterator();
while (keyFirstItr.hasNext()) {
String keyTemp = keyFirstItr.next();
if (first.get(keyTemp).equals(second.get(keyTemp))) { // If same key, same value mapped
output.put(keyTemp, first.get(keyTemp)); // add key value to map
}
}
return output;
}
Simpler solution to this exercise is to skip iterator and use for loop as below. For every name in map1 we check if it exists in map2 and if the values are matching. Then the K, and V are added to the new map:
public static Map intersect(Map<String, Integer> map1, Map<String, Integer> map2){
Map<String, Integer> newMap = new HashMap<>();
for (String name : map1.keySet()){
if(map2.containsKey(name) && map1.get(name).equals(map2.get(name))){
newMap.put(name, map1.get(name));
}
}
return newMap;
}
I have a map like this
Map map=new HashMap();//HashMap key random order.
map.put("a",10);
map.put("a",20);
map.put("a",30);
map.put("b",10);
System.out.println("There are "+map.size()+" elements in the map.");
System.out.println("Content of Map are...");
Set s=map.entrySet();
Iterator itr=s.iterator();
while(itr.hasNext())
{
Map.Entry m=(Map.Entry)itr.next();
System.out.println(m.getKey()+"\t"+m.getValue()+"\t"+ m.hashCode());
}
Output of the above program is
There are 2 elements in the map.
Content of Map are...
b 10 104
a 30 127
Now I want that key a should have multiple values like
a 10
a 20
a 30
So that I should get all the values associated by a. Please advise how can I achieve that same thing. By nesting of collections, I want key 'a' to have all the three values.
Have you checked out Guava Multimaps ?
A collection similar to a Map, but which may associate multiple values
with a single key. If you call put(K, V) twice, with the same key but
different values, the multimap contains mappings from the key to both
values.
If you really want to use standard collections (as suggested below), you'll have to store a collection per key e.g.
map = new HashMap<String, Collection<Integer>>();
Note that the first time you enter a new key, you'll have to create the new collection (List, Set etc.) before adding the first value.
To implement what you want using the Java standard library, I would use a map like this:
Map<String, Collection<Integer>> multiValueMap = new HashMap<String, Collection<Integer>>();
Then you can add values:
multiValueMap.put("a", new ArrayList<Integer>());
multiValueMap.get("a").add(new Integer(10));
multiValueMap.get("a").add(new Integer(20));
multiValueMap.get("a").add(new Integer(30));
If this results uncomfortable for you, consider wrapping this behaviour in a dedicated Class, or using a third-party solution, as others have suggested here (Guava Multimap).
You shouldn't ignore the generic parameters. What you have is
Map<String, Integer> map = new HashMap<>();
if you want to code the solution yourself, you need
Map<String, List<Integer>> map = new HashMap<>();
Anyhow, the preffered way is to use a Guava Multimap
Put an ArrayList instance in the value part.
void addValue(Map map, Object key, Object value) {
Object obj = map.get(key);
List list;
if (obj == null) {
list = new ArrayList<Object>();
} else {
list = ((ArrayList) obj);
}
list.add(value);
map.put(key, list);
}
For More Info check this.
Use Map with value type as list of values..For example, in your map, while adding an entry, you will put key as "a" and you will have to add it's value as a list of Integer , having all the required values, like 1,2,3,4.
For a Map with entries with same key, has no sense to use get() .But as long as you use iterator() or entrySet() this should work:
class HashMap<String, String> {
Set<Entry<String, String>> entries;
#Override
public Set<Entry<String, String>> entrySet() {
return entries;
}
#Override
public int size() {
return entries.size();
}
public String put(String key, String value) {
if (entries == null) {
entries = new AbstractSet<Entry<String, String>>() {
ArrayList<Entry<String, String>> list = new ArrayList<>();
#Override
public Iterator<Entry<String, String>> iterator() {
return list.iterator();
}
#Override
public int size() {
return list.size();
}
#Override
public boolean add(Entry<String, String> stringStringEntry) {
return list.add(stringStringEntry);
}
};
}
StatusHandler.MyEntry entry = new StatusHandler.MyEntry();
entry.setKey(key);
entry.setValue(value);
entries.add(entry);
return value;
}
};
TL;DR So, what is it useful for? That comes from a hack to redmine-java-api to accept complex queries based on form params:
https://stackoverflow.com/a/18358659/848072
https://github.com/albfan/RedmineJavaCLI/commit/2bc51901f2f8252525a2d2258593082979ba7122