I'm new to Java, so sorry if this is pretty obvious, but I can't quite understand how to work with 2 HashMaps inside each other
I have my main, where I want to add some words to a Map, and then, I want to read them:
public static void main(String[] args) {
Dicionario d = new Dicionario();
d.add("english", "BOOK", "Book");
d.add("french", "BOOK", "livre");
d.add("portuguese", "BOOK", "livro");
d.add("english", "YEAR", "year");
d.add("french", "YEAR", "an");
d.add("portuguese", "YEAR", "ano");
System.out.println(d);
}
This Map, has another Map inside him:
private Map<String, Map<String, String> > dic = new HashMap<>();
Then I add those words:
protected void add(String s1, String s2, String s3){
Map<String, String> m = new HashMap<>();
m.put(s2, s3);
dic.put(s1, m);
}
And redefine the function toString to read them, but only appears 1 value per key:
#Override
public String toString(){
String s= "";
for(Map.Entry<String, Map<String,String>> entry : dic.entrySet())
{
s += "\"" + entry.getKey() + "\": ";
for(Map.Entry<String, String> entry2 : dic.get(entry.getKey()).entrySet())
{
s+=entry2.getKey() + "->" + entry2.getValue() + "\t";
}
s+="\n";
}
return s;
}
Why is that? I am looking at this like if it was a bidimensional array, but with 2 values (key, value) in each position.
How can I do to show all the values that the keys from the first map have?
Thanks, and sorry for such a basic question.
You need to modify your add method to following
protected void add(String s1, String s2, String s3) {
Map<String, String> m = null;
m = dic.get(s1);
if (m == null) {
m = new HashMap<>();
}
m.put(s2, s3);
dic.put(s1, m);
}
The problem is that in your add(String, String, String) method, you are instancing a new HashMap each time so you overwrite the previously instanced HashMap from a previous call.
You should update your method this way:
protected void add(String s1, String s2, String s3){
Map<String, String> m = dic.get(s1);
if (m == null) {
m = new HashMap<>();
dic.put(s1, m);
}
m.put(s2, s3);
}
To avoid having to manage this by hand yourself, I suggest that you use Guava's Table data structure (more specifically HashBasedTable).
Related
I have this map on my YamlFile class, which stores all the keys of the file on this format: String key = "firskey.secondkey.thirdkey", Object value = "Example value"
private static Map<String, Object> deepkeymap;
Now, I want to convert my deepkeymap to a nested map that works like this: {firstkey={secondkey={thirdkey="Example value"}}}, my deepkeymap actually stores 4 keys with 4 values (the amount of keys and values will change). I have kind of accomplished this, but not totally as it only converts the last key and value of my deepkeymap, in fact, the example I've put is the output of my nested map, here is the code:
public void save() {
try {
Map<String, Object> datamap = new HashMap<String, Object>();
for(String key : deepkeymap.keySet()) {
Object value = deepkeymap.get(key);
int end = key.length();
for(int start; (start = key.lastIndexOf('.', end - 1)) != -1; end = start) {
value = new HashMap<>(Collections.singletonMap(key.substring(start + 1, end), value));
}
datamap.putAll(new HashMap<>(Collections.singletonMap(key.substring(0, end), value)));
}
System.out.println("Datamap: "+datamap);
} catch (IOException e) {
e.printStackTrace();
}
}
As mentioned above, output is:
Datamap: {firstkey={secondkey={thirdkey="Example value"}}}
But it should have another 3 keys as deepkeymap contains 4 keys with their respective 4 values, I have already checked they are stored on it and no one has a null value, doing a debug on the keySet loop printing keys and values.
You can play with the code below. Factory method fails for incorrect input, also, there is only toString for the DeepKeyMap. Below is a JUnit test, that just runs the code and tests nothing. You can extract the DeepKeyMap into a seperate class if you will use it in the future.
public class MapTest
{
static class DeepKeyMap
{
Map<String, Object> map = new HashMap<>();
public void put(
String path,
Object value
)
{
String[] split = path.split("\\.");
this.put(split, value);
}
public void put(
String[] path,
Object value
)
{
Map<String, Object> deepestMap = createMapsToTheDeepestKey(path);
String deepestKey = path[path.length - 1];
deepestMap.put(deepestKey, value);
}
private Map<String, Object> createMapsToTheDeepestKey(String[] path)
{
Map<String, Object> deepestMap = map;
for (int i = 0; i < path.length - 1; i++)
{
String key = path[i];
deepestMap = getDeeperMap(deepestMap, key);
}
return deepestMap;
}
private Map<String, Object> getDeeperMap(
Map<String, Object> deepestMap,
String key
)
{
if (!deepestMap.containsKey(key))
{
deepestMap.put(key, new HashMap<>());
}
return (Map<String, Object>) deepestMap.get(key);
}
#Override
public String toString()
{
return map.toString();
}
public static DeepKeyMap from(Map<String, Object> original)
{
DeepKeyMap result = new DeepKeyMap();
// the for loop can be minimized to
// original.forEach(result::put);
for (var entry : original.entrySet())
{
result.put(entry.getKey(), entry.getValue());
}
return result;
}
}
#Test
void test()
{
Map<String, Object> original = Map.of("flat", "First Level",
"nested.value.one", "Second Level",
"nested.value.two", "Third Level",
"nested.different.value.one", "Fourth Level"
);
DeepKeyMap deepMap = DeepKeyMap.from(original);
System.out.println(deepMap);
}
}
Edit: I refactored the code above a bit. Hopefully it is a bit more clear what it does.
I would not use it like that, you can have there identical keys on different levels, which is not the intention of a Map.
Also, you may produce a construct that has as type of key either a map or a string. This brings various uncertainties.
You should consider using other data structures or a database.
What is your intention, maybe we can assist you.
I have a scenario where I need to take the keys of a Map<String, Set<String>>, and add them into a new Set<String> that is sorted. The sort order is based on the Map values for each key. The value for each key of the map is a Set containing other keys that are related to that key.
I need the keys to be sorted in such a way that a related key must be BEFORE another key that contains it in its related Set. To use a programming paradigm, it's similar to requiring a variable to be declared on an earlier line, before it can be referenced on another line.
For example, the following represents the contents of the Map<String, Set<String>>:
abc=[def, ghi, jkl, mno]
def=[]
ghi=[def]
jkl=[ghi, stu]
mno=[]
pqr=[abc]
stu=[def]
vwx=[mno, ghi]
zy0=[jkl]
In this example, the key "jkl" has a relationship to keys, "ghi" and "stu", "def" does not have a relationship to any of the keys.
NOTE: The relationships will be ONE-WAY only. So, for example, if "ghi" is related to "def", "def" will NEVER be related to "ghi".
So, for the above Map, the sort order would be:
def=[]
mno=[]
ghi=[def]
stu=[def]
vwx=[mno, ghi]
jkl=[ghi, stu]
zy0=[jkl]
abc=[def, ghi, jkl, mno]
pqr=[abc]
Here's the Comparator that I wrote. It's inside of a runnable test class that uses the example above:
import java.util.*;
public class RelationshipComparator_Test {
public static void main(String[] args) {
String[] testMap = "abc=[def,ghi,jkl,mno]|def=[]|ghi=[def]|jkl=[ghi,stu]|mno=[]|pqr=[abc]|stu=[def]|vwx=[mno,ghi]|zy0=[jkl]".split("[|]");
Map<String, Set<String>> relationshipMap = new HashMap<>();
for (String entry : testMap) {
String[] keyValue = entry.split("[=]");
String replacement = keyValue[1].replaceAll("[^a-z0-9,]", "");
Set<String> valueSet = new HashSet<>();
String[] values = (!replacement.equals("") ? replacement.split("[,]") : new String[0]);
Collections.addAll(valueSet, values);
relationshipMap.put(keyValue[0], valueSet);
}
Set<String> sortedKeys = new TreeSet<>(new RelationshipComparator(relationshipMap));
sortedKeys.addAll(relationshipMap.keySet());
for (String key : sortedKeys) {
System.out.println(key + "=" + relationshipMap.get(key));
}
}
static class RelationshipComparator implements Comparator<String> {
private Map<String, Set<String>> relationshipMap;
RelationshipComparator(Map<String, Set<String>> relationshipMap) {
this.relationshipMap = relationshipMap;
}
#Override
public int compare(String o1, String o2) {
Set<String> o1Set = relationshipMap.get(o1);
Set<String> o2Set = relationshipMap.get(o2);
if (o1Set != null && o2Set != null) {
if (o1Set.size() == 0 && o2Set.size() > 0) {
printCompare(o1, o2, "o1Set.size() == 0: -1");
return -1;
}
if (o2Set.size() == 0 && o1Set.size() > 0) {
printCompare(o1, o2, "o2Set.size() == 0: 1");
return 1;
}
if (o1Set.contains(o2)) {
printCompare(o1, o2, "o1Set.contains(o2): 1");
return 1;
}
if (o2Set.contains(o1)) {
printCompare(o1, o2, "o2Set.contains(o1): -1");
return -1;
}
}
printCompare(o1, o2, "default: " + o1.compareTo(o2));
return o1.compareTo(o2);
}
private void printCompare(String o1, String o2, String result) {
System.out.println("**********");
System.out.println("o1: " + o1 + "=" + relationshipMap.get(o1));
System.out.println("o2: " + o2 + "=" + relationshipMap.get(o2));
System.out.println("result: " + result);
System.out.println("**********");
System.out.println();
}
}
}
If you run the code, you'll see the following output:
def=[]
mno=[]
ghi=[def]
jkl=[stu, ghi]
abc=[def, ghi, jkl, mno]
pqr=[abc]
stu=[def]
vwx=[ghi, mno]
zy0=[jkl]
It's incorrect because, "jkl" references "stu", but "stu" is sorted after "jkl".
Any help would be greatly appreciated.
You say that relationships are one-way, which rules out obvious cases such as:
a=[b]
b=[a]
for which no solution is possible. However, we also need to rule out cyclic relationships such as:
a=[b]
b=[c]
c=[a]
If this is the case then I believe you can achieve the required ordering by using a PriorityQueue to order keys by the size of the value set related to the key. As keys are removed from the queue they also have to be removed from any of the related value sets that contain them. Which value sets contain a given key can be recovered from a reverse Map<String, Set<String>> which holds the set of keys that refer to a given value key.
Hopefully some code will make things clearer:
static List<String> orderByRef(Map<String, Set<String>> relationshipMap)
{
final Map<String, Set<String>> relationshipMapCopy = new HashMap<>();
for(String key : relationshipMap.keySet())
relationshipMapCopy.put(key, new HashSet<>(relationshipMap.get(key)));
final Map<String, Set<String>> referencedBy = new HashMap<>();
for(String key : relationshipMap.keySet())
referencedBy.put(key, new HashSet<>());
for (Entry<String,Set<String>> e : relationshipMapCopy.entrySet())
for(String v : e.getValue())
referencedBy.get(v).add(e.getKey());
PriorityQueue<String> pq = new PriorityQueue<>(new Comparator<String>()
{
#Override
public int compare(String k1, String k2)
{
return relationshipMapCopy.get(k1).size() - relationshipMapCopy.get(k2).size();
}
});
pq.addAll(relationshipMap.keySet());
List<String> orderedKeys = new ArrayList<>();
while(!pq.isEmpty())
{
String minKey = pq.poll();
if(!relationshipMapCopy.get(minKey).isEmpty())
{
// cyclic relationship
break;
}
orderedKeys.add(minKey);
for(String refKey : referencedBy.get(minKey))
{
// remove minKey from value set of refKey
relationshipMapCopy.get(refKey).remove(minKey);
// reorder refKey in pq
pq.remove(refKey);
pq.add(refKey);
}
}
return orderedKeys;
}
Note that since we're modifying the relationshipMap by removing keys from value sets we first need to create a deep copy. Also, we can detect the presence of a cyclic relationships by checking that the value set of the min key is empty.
Output:
def []
mno []
stu [def]
ghi [def]
vwx [ghi, mno]
jkl [stu, ghi]
zy0 [jkl]
abc [def, ghi, jkl, mno]
pqr [abc]
Which satisfies the constraint that no key is referenced before it appears in the list.
For input containing a cyclic relationship, eg (z=[y]|y=[]|a=[b]|b=[c]|c=[a]), we get:
y []
z [y]
I have a list of maps. Which I cannot predict the key of each map. key can be an any two digits number which will change acordingly to the request.But values contains unique values from request to request.Now I need to sort this list of maps according to the value of each key. I've already tried something like bellow,
List<Map<String,String>> listOfMaps = new ArrayList<>();
Map<String,String> map1 = new HashMap<>();
Map<String,String> map2 = new HashMap<>();
map1.put("key1","value1");
map2.put("key2","value1");
listOfMaps.add(map1);
listOfMaps.add(map2);
sort(listOfMaps);
Collections.sort(listOfMaps, new Comparator<Map<String, String>>() {
public int compare(final Map<String, String> o1, final Map<String, String> o2) {
return o1.get("key").compareTo(o2.get("key"));
}
});
Since "key" can be differ from map to map, seems I couldnt use the above code.
I've also tried these examples.
How to Sort a List of Maps by two Categories?
Sorting list of maps based on a value
Since I'm cannot predict the key, I couldnt find an answer from these post.Can any one help with this?.
The below example should work, under the assumption that each map element contains only one entry:
List<Map<String, String>> list = new ArrayList<>();
//Add entries
Collections.sort(list, new Comparator<Map<String, String>>() {
public int compare(Map<String, String> o1, Map<String, String> o2) {
Collection<String> values1 = o1.values();
Collection<String> values2 = o2.values();
if(!values1.isEmpty() && !values2.isEmpty()){
return values1.iterator().next().compareTo(values2.iterator().next());
}else{
return 0;
}
}
});
While it is occasionally necessary to create a Map with only one key and one value (Collections.singletonMap is used for this), it doesn't look like Map is appropriate for your situation. It makes much more sense to use a custom class with two fields, something like this:
// This is immutable. You may prefer to make fields non-final and add setters.
// You may also want to override equals() and hashCode().
final class StringPair {
private final String key;
private final String value;
StringPair(String key, String value) {
this.key = key;
this.value = value;
}
#Override
public String toString() {
return "[" + key + ", " + value + "]";
}
}
Then you can do:
List<StringPair> list = new ArrayList<>();
list.add(new StringPair("K2", "V2"));
list.add(new StringPair("K1", "V1"));
Collections.sort(list, new Comparator<StringPair>()
#Override
public int compare(StringPair o1, StringPair o2) {
return o1.value.compareTo(o2.value);
}
});
System.out.println(list); // Prints [[K1, V1], [K2, V2]]
This is a very generic question. I have a hashmap and I want to print it in a tabular format dynamically without knowing the size of the contents beforehand. The table should be well spaced just like we see in a Db query result. Are there any libraries/utilities which directly helps in this type of conversion? Or does JAVA have some intrinsic functions which I could make use of?
The code which I have written is a very naive one, and does not cater to dynamic length of the strings. I need the rows to be aligned also.
StringWriter returnString = new StringWriter();
Map<String,HashMap<String, String>> hashMap = new HashMap<String, HashMap<String, String>>();
for (Entry e : hashMap.entrySet()) {
HashMap<String, Number> hm = (HashMap<String, Number>) e.getValue();
String key = (String) e.getKey();
returnString.append("|\t").append(key).append("\t|");
for (Entry en : hm.entrySet()){
returnString.append("|\t").append((String) en.getValue()).append("\t|");
}
returnString.append("\r\n");
}
return returnString.toString();
The output should be like this irrespective of the strings length
s1 | s3 | s4
askdkc | asdkask | jksjndan
It looks like you already have the iteration figured out and are just working on the formatting. You could put it into a TableModel, and let the JTable handle the tabular formatting.
You could select fixed column widths or iterate once over the entries to find the maximum length of each column, then again to print them with appropriate padding.
Another option would be to extend HashMap so that it records the longest key and value as entries are added:
package com.example;
public class MyHashMap<K, V> extends java.util.HashMap<K, V> {
private int maxKeyLength = 0;
private int maxValueLength = 0;
#Override
public V put(K key, V value) {
maxKeyLength = Math.max(maxKeyLength, key.toString().length());
maxValueLength = Math.max(maxValueLength, value.toString().length());
return value;
};
public int getMaxKeyLength() {
return maxKeyLength;
}
public int getMaxValueLength() {
return maxValueLength;
}
}
Note this ignores the obvious case where you also remove items--depending on your usage pattern, you'll have to do a little or a lot more work if you also want to shrink the columns when removing entries with the longest keys/values.
I have written a small code that will print the HashMap similar (not exactly) to how query results are printed (like sqlplus). Just sharing here so that it might help some one.
List<Map<String, Object>> resultSet = jdbcTemplate.queryForList(selectQuery);
Map<String, Integer> maxColSizeMap = new HashMap<String, Integer>();
boolean initMaxColSizeMap = true;
for (Map<String, Object> map : resultSet) {
for (String key : map.keySet()) {
if (initMaxColSizeMap) {
String ColValue = (map.get(key) == null ) ? "" : map.get(key).toString();
Integer whoIsBig = Math.max(ColValue.length(), key.length());
maxColSizeMap.put(key, whoIsBig);
} else {
String ColValue = (map.get(key) == null ) ? "" : map.get(key).toString();
Integer whoIsBig = Math.max(ColValue.length(), key.length());
whoIsBig = Math.max(maxColSizeMap.get(key), whoIsBig);
maxColSizeMap.put(key, whoIsBig);
}
}
initMaxColSizeMap = false;
}
// Column HEADER
for (Map<String, Object> map : resultSet) {
System.out.println("");
StringBuilder colName = new StringBuilder();
StringBuilder underLine = new StringBuilder();
for (String key : map.keySet()) {
colName.append(StringUtils.rightPad(key, maxColSizeMap.get(key)));
colName.append(" ");
underLine.append(StringUtils.repeat('-', maxColSizeMap.get(key)));
underLine.append(" ");
}
// Do one time only
System.out.println(colName.toString());
System.out.println(underLine.toString());
break;
}
// Print the rows
for (Map<String, Object> map : resultSet) {
StringBuilder row = new StringBuilder();
for (String key : map.keySet()) {
String str = map.get(key) == null ? "" : map.get(key).toString();
row.append(StringUtils.rightPad(str, maxColSizeMap.get(key) + 1));
}
System.out.println(row);
}
System.out.println("");
Note: org.apache.commons.lang3.StringUtils is used for padding.
This one is not an exact answer to the question, so tweak the code as required.
You can use a for-each loop and just print it out, correct? No need to have the size..
Whatever is meant by "table" though?
How print out the contents of a HashMap<String, String> in ascending order based on its values?
You may want to get all the keys of the map, iterate the keys and print the details e.g.
Map<String, String> valueMap = new HashMap<String, String>();
.....
Set<String> keys = valueMap.keySet();
Iterator<String> iter = keys.iterator();
while(iter.haxNext()){
String key = iter.next();
System.out.println("\t"+key+"|\t"+valueMap.get(key));
}
EDIT: If you want specific width then consider using Apache StringUtils as below:
int MAXWIDTH = 20; //<- set this with the max width of the column
Map<String, String> valueMap = new HashMap<String, String>();
.....
Set<String> keys = valueMap.keySet();
Iterator<String> iter = keys.iterator();
while(iter.haxNext()){
String key = iter.next();
System.out.println(StringUtils.rightPad(key, MAXWIDTH)+ "|"+
StringUtils.rightPad(valueMap.get(key), MAXWIDTH));
}
I want to merge Two HashMaps.
I could use map1.putAll(map2); but I don't want to overwrite the key's as yes they will have conflicting keys.
So the keys in each map will be like this
word1 word1
word2 word2
word3 word3
and when I merge them I would like:
word1
word2
word3
word4
word5
word6
It can just overwrite the keys, aslong as the keys are incremental and use the first key text i.e. reads one of the pairs and extracts 'word' so each would be word1 word2.
But the other caveat I was thinking of the mobile environment and what I can do without having to put up a loading screen or even capable of.
So as a starter I suppose:
HashMap<String, Object> hm1 = new HashMap<String, Object>();
hm1.put("key1", "a");
hm1.put("key2", "a");
hm1.put("key3", "a");
HashMap<String, Object> hm2 = new HashMap<String, Object>();
hm2.put("key1", "1");
hm2.put("key2", "2");
hm2.put("key3", "3");
HashMap<String, Object> newHM = new HashMap<String, Object>();
String keyWord = "";
for (String s: hm1.keySet()) {
keyWord = s;
break;
}
int count = 0;
for (Object o : hm1.values()) {
newHM.put(keyWord+count, o);
}
for (Object o : hm2.values()) {
newHM.put(keyWord+count, o);
}
But I'm wondering, how efficient is this? It looks correct, And is there a better way to do it? I don't want to use extra object's unnecessarily
If your keys are incremental an basically represent a simple index, you should use a List.
You could try your own implementation of List which will also store a keyword.
class KeyWordedArrayList<T> extends ArrayList<T>{
private final String keyword;
public KeyWordedArrayList(String keyword){
this.keyword = keyword;
}
public String getKeyword(){
return keyword;
}
}
You can also do an implementation of Map :
class KeyWordedMap<T> extends HashMap<Integer, T> {
private final String keyword;
public KeyWordedMap(String keyword) {
this.keyword = keyword;
}
public String getKeyword() {
return keyword;
}
#Override
public void putAll(Map<? extends Integer, ? extends T> m) {
for (Map.Entry<? extends Integer, ? extends T> entry : m.entrySet()) {
int i = entry.getKey();
while (this.containsKey(i)) {
i++;
}
this.put(i, entry.getValue());
}
}
}
To match my example it would be:
#Override
public void putAll(Map<? extends String, ? extends Object> m) {
for (Map.Entry<? extends String, ? extends Object> entry : m.entrySet()) {
String keyWord = "";
for (String s: this.keySet()) {
keyWord = s.substring(0, s.length()-1);
break;
}
int i = 0;
while (this.containsKey(i)) {
i++;
}
this.put(keyWord +i, entry.getValue());
}
}