Print HashMap in formatted table format dynamically - java

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));
}

Related

Convert map to nested map

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.

Reversing key value pairs in a HashMap

I have a data structure as follows:
Map<String,ArrayList<String>> graph = new HashMap<String,ArrayList<String>>();
This is essentially a hash map which puts string values as keys and stores array list of strings in the value for the keys.
Now I am trying to reverse the key value pattern to make value the key and key the value. The way I am doing it is as follows:
private Map<String,ArrayList<String>> reverseAdjList(Map<String,ArrayList<String>> adjList){
Map<String,ArrayList<String>> tGraph = new HashMap<String,ArrayList<String>>();
for (Map.Entry<String, ArrayList<String>> entry : adjList.entrySet()) {
String key = entry.getKey();
ArrayList<String> values = new ArrayList<>();
values.add(key);
ArrayList<String> value = entry.getValue();
for(String v:value){
if(tGraph.containsKey(v)){
values.addAll(tGraph.get(v));
}
tGraph.put(v, values);
}
}
return tGraph;
}
So this works for me in reversing the hash map keys values pattern for small data set however when I try it on a larger dataset I run into
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3210)
at java.util.Arrays.copyOf(Arrays.java:3181)
at java.util.ArrayList.grow(ArrayList.java:261)
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.addAll(ArrayList.java:579)
at GraphProcessor.reverseAdjList(GraphProcessor.java:67)
at GraphProcessor.SCC(GraphProcessor.java:135)
at GraphProcessor.<init>(GraphProcessor.java:50)
at GraphProcessor.main(GraphProcessor.java:250)
I know this is a very naïve and wrong approach to do it, what is a better and correct way to do it?
There's a bug in your code:
for (Map.Entry<String, ArrayList<String>> entry : adjList.entrySet()) {
String key = entry.getKey();
ArrayList<String> values = new ArrayList<>(); // Wrong place for this variable.
values.add(key);
ArrayList<String> value = entry.getValue();
for(String v:value){
if(tGraph.containsKey(v)){
values.addAll(tGraph.get(v));
}
tGraph.put(v, values);
}
}
The local variable values should be in the nested for loop, otherwise values are accumulated for all later new key v and will cost a lot of memory if your dataset is large, it should be:
private Map<String, ArrayList<String>> reverseAdjList(Map<String, List<String>> adjList) {
Map<String, ArrayList<String>> tGraph = new HashMap<>();
for (Map.Entry<String, List<String>> entry : adjList.entrySet()) {
String key = entry.getKey();
List<String> value = entry.getValue();
for (String v : value) {
ArrayList<String> values = new ArrayList<>();
values.add(key);
if (tGraph.containsKey(v)) {
values.addAll(tGraph.get(v));
}
tGraph.put(v, values);
}
}
return tGraph;
}
But actually you don't need to create a new List instance for each inner for step, try the following code with JDK 1.8:
private Map<String, List<String>> reverseMap(Map<String, List<String>> adjList) {
Map<String, List<String>> tGraph = new HashMap<>();
for (Map.Entry<String, List<String>> entry : adjList.entrySet()) {
for (String value : entry.getValue()) {
tGraph.computeIfAbsent(value, v -> new ArrayList<>()).add(entry.getKey()); // Updated according comment from #shmosel
}
}
return tGraph;
}
If you're using older version of jdk, you can try:
private Map<String, List<String>> reverseMap(Map<String, List<String>> adjList) {
Map<String, List<String>> tGraph = new HashMap<>();
for (Map.Entry<String, List<String>> entry : adjList.entrySet()) {
for (String value : entry.getValue()) {
List<String> newValues = tGraph.get(value);
if (newValues == null) {
newValues = new ArrayList<>();
tGraph.put(value, newValues);
}
newValues.add(entry.getKey());
}
}
return tGraph;
}
Hope this could be helpful :-)

Hashmap keySet() returns the value instead of the key

Here is my method.In the hashmap I have pairs of Country - Capital(Russia - Moscow) for Europe.However, it keeps returning me the value(the capital) instead of the key(the country).I tested my hashmap and it is in the correct order.
Map<String, String> europe1 = new HashMap<String, String>()
public String randomCountry(Map theMap)
{
Random generator = new Random();
List<String> keys = new ArrayList<String>(theMap.keySet());
String randomKey = keys.get(generator.nextInt(keys.size()));
Object theKeyValue = (String )theMap.get(randomKey);
System.out.println(theKeyValue);
return (String) theKeyValue;
}
If I do this:
for ( String key : europe1.keySet() )
{ System.out.println( key ); }
I get my countries printed.
Any ideas why my method does not work as expected?
You are fetching the value here :
Object theKeyValue = (String )theMap.get(randomKey);
If you want randomCountry to return the key, you should return randomKey instead of theKeyValue.
Object theKeyValue = (String )theMap.get(randomKey);
you can return randomKey.
Please find HashMap documentation link for more details regarding keySet() : to return only Key and entrySet() : to return key value pair.
https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html

Retrieve all values from HashMap keys in an ArrayList Java

Good day, this is kind of confusing me now(brain freeze!) and seem to be missing something. Have an ArrayList which i populate with a HashMap. now i put in my HashMap and arraylist.
Map.put(DATE, value1);
Map.put(VALUE, value2);
arraylist.put(Map);
Since am parsing a JSON, the arraylist increases in significant size. now my question is how do you get the values from both map keys in the arraylist? i have tried this
if(!list.isEmpty()){ // list is an ArrayList
for(int k = 0; k < list.size(); k++){
map = (HashMap)list.get(k);
}
}
Log.d(TAG, "map size is" + map.size());
String [] keys = new String[map.size()];
String [] date_value = new String[map.size()];
String [] value_values = new String[map.size()];
int i = 0;
Set entries = map.entrySet();
Iterator iterator = entries.iterator();
while(iterator.hasNext()){
Map.Entry mapping = (Map.Entry)iterator.next();
keys[i] = mapping.getKey().toString();
date_value[i] = map.get(keys[i]);
if(keys[i].equals(DATE)){
date_value[i] = map.get(keys[i]);
} else if(keys[i].equals(VALUE)){
value_values[i] = map.get(keys[i]);
}
i++;
}
But i can't seem to get all the values. the Map size always return a value of 2, which is just the elements. how can i get all the values from the Map keys in the ArrayList? Thanks
Why do you want to re-invent the wheel, when you already have something to do your work. Map.keySet() method gives you a Set of all the keys in the Map.
Map<String, Integer> map = new HashMap<String, Integer>();
for (String key: map.keySet()) {
System.out.println("key : " + key);
System.out.println("value : " + map.get(key));
}
Also, your 1st for-loop looks odd to me: -
for(int k = 0; k < list.size(); k++){
map = (HashMap)list.get(k);
}
You are iterating over your list, and assigning each element to the same reference - map, which will overwrite all the previous values.. All you will be having is the last map in your list.
EDIT: -
You can also use entrySet if you want both key and value for your map. That would be better bet for you: -
Map<String, Integer> map = new HashMap<String, Integer>();
for(Entry<String, Integer> entry: map.entrySet()) {
System.out.println(entry.getKey());
System.out.println(entry.getValue());
}
P.S.: -
Your code looks jumbled to me. I would suggest, keep that code aside, and think about your design one more time. For now, as the code stands, it is very difficult to understand what its trying to do.
List constructor accepts any data structure that implements Collection interface to be used to build a list.
To get all the keys from a hash map to a list:
Map<String, Integer> map = new HashMap<String, Integer>();
List<String> keys = new ArrayList<>(map.keySet());
To get all the values from a hash map to a list:
Map<String, Integer> map = new HashMap<String, Integer>();
List<Integer> values = new ArrayList<>(map.values());
Try it this way...
I am considering the HashMap with key and value of type String, HashMap<String,String>
HashMap<String,String> hmap = new HashMap<String,String>();
hmap.put("key1","Val1");
hmap.put("key2","Val2");
ArrayList<String> arList = new ArrayList<String>();
for(Map.Entry<String,String> map : hmap.entrySet()){
arList.add(map.getValue());
}
Create an ArrayList of String type to hold the values of the map. In its constructor call the method values() of the Map class.
Map <String, Object> map;
List<Object> list = new ArrayList<Object>(map.values());
Put i++ somewhere at the end of your loop.
In the above code, the 0 position of the array is overwritten because i is not incremented in each loop.
FYI: the below is doing a redundant search:
if(keys[i].equals(DATE)){
date_value[i] = map.get(keys[i]);
} else if(keys[i].equals(VALUE)){
value_values[i] = map.get(keys[i]);
}
replace with
if(keys[i].equals(DATE)){
date_value[i] = mapping.getValue();
} else if(keys[i].equals(VALUE)){
value_values[i] = mapping.getValue()
}
Another issue is that you are using i for date_value and value_values. This is not valid unless you intend to have null values in your array.
This is incredibly old, but I stumbled across it trying to find an answer to a different question.
my question is how do you get the values from both map keys in the arraylist?
for (String key : map.keyset()) {
list.add(key + "|" + map.get(key));
}
the Map size always return a value of 2, which is just the elements
I think you may be confused by the functionality of HashMap. HashMap only allows 1 to 1 relationships in the map.
For example if you have:
String TAG_FOO = "FOO";
String TAG_BAR = "BAR";
and attempt to do something like this:
ArrayList<String> bars = ArrayList<>("bar","Bar","bAr","baR");
HashMap<String,String> map = new HashMap<>();
for (String bar : bars) {
map.put(TAG_BAR, bar);
}
This code will end up setting the key entry "BAR" to be associated with the final item in the list bars.
In your example you seem to be confused that there are only two items, yet you only have two keys recorded which leads me to believe that you've simply overwritten the each key's field multiple times.
Suppose I have Hashmap with key datatype as KeyDataType
and value datatype as ValueDataType
HashMap<KeyDataType,ValueDataType> list;
Add all items you needed to it.
Now you can retrive all hashmap keys to a list by.
KeyDataType[] mKeys;
mKeys=list.keySet().toArray(new KeyDataType[list.size()]);
So, now you got your all keys in an array mkeys[]
you can now retrieve any value by calling
list.get(mkeys[position]);
Java 8 solution for produce string like "key1: value1,key2: value2"
private static String hashMapToString(HashMap<String, String> hashMap) {
return hashMap.keySet().stream()
.map((key) -> key + ": " + hashMap.get(key))
.collect(Collectors.joining(","));
}
and produce a list simple collect as list
private static List<String> hashMapToList(HashMap<String, String> hashMap) {
return hashMap.keySet().stream()
.map((key) -> key + ": " + hashMap.get(key))
.collect(Collectors.toList());
}
It has method to find all values from map:
Map<K, V> map=getMapObjectFromXyz();
Collection<V> vs= map.values();
Iterate over vs to do some operation

Finding all keys in a HashMap with a character an replace it

I have a HashMap and now i need to find all the keys inside the HashMap which has a particular letter inside it and replace it with another letter
You could try this :
public void replaceKeysChar(char originalChar, char newChar, Map<String, ?> map) {
Map<String, Object> tempMap = new HashMap<String, Object>();
for (Map.Entry<String, ?> entry : map.entrySet()) {
String key = entry.getKey();
if(key != null){
key = key.replace(originalChar, newChar);
}
tempMap.put(key, entry.getValue());
}
map.clear();
map.putAll(tempMap);
}
This way you handle only char, and you don't change the implementation.
Plus when you iterate you don't add item in your map (it would be a bad idea).
If you don't care about the implementation simply return the tempMap and remove the clear/putAll part (it will consume less resources).
EDIT :
After #locka's answer I think I should specify that this method can't handle collisions.
If your Map contains the keys "toto" and "tata" and you do a `replaceKeysChar('a','o', map) only one of the values between "toto"'s value and "tata"'s value will be in the map, the other will simply be ignored.
EDIT bis:
To handle collisions with exceptions (à la #Stephen C) just replace the old for by this one :
for (Map.Entry<String, ?> entry : map.entrySet()) {
String key = entry.getKey();
if(key != null){
key = key.replace(originalChar, newChar);
}
if(tempMap.containsKey(key))
throw new CollisionException();
tempMap.put(key, entry.getValue());
}
Here's a solution that "deals with" collisions by throwing an exception.
public void replaceKeysChar(char originalChar, char newChar, Map<String, ?> map) {
Map<String, Object> tempMap = new HashMap<String, Object>();
Set<String> tempSet = new HashSet<String>();
for (Map.Entry<String, ?> entry : map.entrySet()) {
String originalKey = entry.getKey();
String newKey = originalKey .replace(originalChar, newChar);
if (!newKey.equals(originalKey)) {
if (map.containsKey(newKey) || tempMap.containsKey(newKey)) {
throw new CollisionException(newKey);
}
tempMap.put(newKey, entry.getValue());
tempSet.add(originalKey());
}
}
map.keySet().removeAll(tempSet);
map.putAll(tempMap);
}
EDIT
Fixed bugs ... in previous versions.
Construct a new hashmap
Copy all the
(key,value) pairs from the old
hashmap, except that you replace
OLDCHAR with NEWCHAR in the keys
Swap
the hashmaps over.
So you want to change the Key. If the Hashalgorithm isn't something strange you'll have to get all key-value pairs, delete them from the hashmap, change them and reinsert them. Or insert them in a new Hashmap.
I think people are concerned that you can't change a String key in situ because of immutability and the String's hashcode being tied the value of the string. Adding / removing keys could potentially work but then you run the risk of collisions, where you end up replacing an existing string by accident.
The most obvious way to solve this issue is to use a custom key, e.g.
public class MyKey {
static private Random r = new Random();
private String keyValue;
private final int hashCode = r.nextInt();
#Override
public int hashCode() {
return hashCode;
}
public void setKeyValue(String keyValue) {
this.keyValue = keyValue;
}
public String getKeyValue() {
return keyValue;
}
}
This key has a string key value which you can easily set / get and a hashCode which is just a random number. Once you add this key to the hash map you can change the string in situ without adding or removing the key. All you need do is iterate the key set and perform any processing you like on the key, all without affecting the hashcode or running the risk of collisions or anything else.

Categories

Resources