Java: can't use Map.Entry to iterate over a Map? - java

Every Java Map iteration example I've seen recommends this paradigm:
for (Map.Entry<String, String> item : hashMap.entrySet()) {
String key = item.getKey();
String value = item.getValue();
}
However, when I attempt to do this I get a warning from my compiler:
Incompatible types: java.lang.Object cannot be converted to java.util.Map.Entry<java.lang.String, java.lang.Object>
Here's my code - the only wrinkle I see is that I'm iterating over an array of Map objects, and then iterating over the elements of the individual Map:
result = getArrayOfMaps();
// Force to List LinkedHashMap
List<LinkedHashMap> result2 = new ArrayList<LinkedHashMap>();
for (Map m : result) {
LinkedHashMap<String, Object> n = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : m.entrySet()) {
n.put(entry.getKey(),entry.getValue());
}
result2.add(n);
}
Am I missing something blatantly obvious?

This is happening because you are using raw types: a List<LinkedHashMap> instead of a List<LinkedHashMap<Something, SomethingElse>>. As a result, the entrySet is just a Set instead of a Set<Map.Entry<Something, SomethingElse>>. Don't do that.

Related

List manipulation and conversion in Java

I have an input list like:
var list = Arrays.asList("etc->hosts", "etc->dockers->ssl->certs", "root");
And I am trying to convert it into nested map:
{etc={dockers={ssl={certs={}}}, hosts={}}, root={}}
I tried to split the key from input list with dot i.e. '->' and tried to iterate over to construct a map: Map<String, Map>.
Tried.groupingBy() .computeIfAbsent() .stream().map().filter().collect(), both of them failed.
You don't need streams, just a simple for loop that walks down each chain:
Map<String, Object> map = new HashMap<>();
for (String chain : list) {
Map<String, Object> current = map;
for (String node : chain.split("->")) {
current = (Map<String, Object>)current.computeIfAbsent(node, n -> new HashMap<>());
}
}
If you want to avoid the unchecked cast warnings, you can define a self-referential map like this (assuming you don't plan to mix in additional value types):
class NestingMap extends HashMap<String, NestingMap> {}
Ideone Demo

How can I convert a 'Collection<Map<String,String>>' to 'Map<String,String>'?

I want to convert from Collection<Map<String,String>> to Map<String,String>.
When I tried to do this way,
Map<String,String> m = (Map<String,String>)map.values();
where,
map is of type Map<String,Map<String,String>>
I'm getting
java.lang.ClassCastException: java.util.TreeMap$Values cannot be cast to java.util.Map
What is it trying to say? I'm not able to get it and how do I correctly convert from Collection<Map<String,String>> to Map<String,String>?
You can use this small snippet to put all the values into a single map:
Map<String, String> result = new TreeMap<>();
for(Map<String, String> value : map.values()) {
result.putAll(value);
}
Though this will just overwrite duplicate keys with a new value if there are any.
As long as it's collection then you should think as it's collection of objects.
Then proceed the iteration, for each object, you shall put it in the map
public Map<String, String> getMapsFromArrayOfMaps( Collection<Map<String,String>> maps ) {
Map<String, String> result = new HashMap<>();
maps.forEach(map->result.putAll(map));
return result ;
}

How to add a value to a list of values for a single key in a hashmap (Java)

I have written this:
HashMap<String, String> map1 = new HashMap<String, String>();
Map<String, ArrayList<String>> map2 = new HashMap<String, ArrayList<String>>();
i am trying to allow more then 1 value for each key in a hashmap. so if the first key is '1', i want to allow '1' to be paired with values '2' and '3'.
so it be like:
1 --> 2
|--> 3
but when I do:
map2.put(key, value);
it gives error that says "incompatible types" and it can not be converted to ArrayList and it says the error is at the value part of the line.
If you are using Java 8, you can do this quite easily:
String key = "someKey";
String value1 = "someValue1";
String value2 = "someValue2";
Map<String, List<String>> map2 = new HashMap<>();
map2.computeIfAbsent(key, k -> new ArrayList<>()).add(value1);
map2.computeIfAbsent(key, k -> new ArrayList<>()).add(value2);
System.out.println(map2);
The documentation for Map.computeIfAbsent(...) has pretty much this example.
In map2 you need to add ArrayList (you declared it as Map<String, ArrayList<String>> - the second one is the value type) only, that's why it gives you incompatible types.
You would need to do initialize the key with an ArrayList and add objects to it later:
if (!map2.containsKey(key)) {
map2.put(key, new ArrayList<String>());
}
map2.get(key).add(value);
Or you could use Multimap from guava, then you can just map2.put and it won't overwrite your values there but add to a list.
You are little bit away from what you are trying to do.
Map<String, ArrayList<String>> map2 = new HashMap<String, ArrayList<String>>();
this will allow only String as key and an ArrayList as value. So you have to try something like:
ArrayList<String> value=new ArrayList<String>();
value.add("2");
value.add("3");
map2.put("1", value);
When retrieving you also have to follow ans opposite procedure.
ArrayList<String> valueTemp=map2.get("1");
then you can iterate over this ArrayList to get those values ("2" and "3");
Try like this. //use list or set.. but set avoids duplicates
Map<String, Set<String>> map = new HashMap<>();
Set<String> list = new HashSet<>();
// add value to the map
Boolean b = map.containsKey(key);
if (b) {
map.get(key).addAll(list);
} else
map.put(key, list);
}
You can not add different values in same key in Map. Map is override the value in that key. You can do like this way.
Map<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
ArrayList<String> list=new ArrayList<String>();
list.add("2");
list.add("3");
map.put("1", list);
first add value in array list then put into map.
It is all because standard Map implementations in java stores only single pairs (oneKey, oneValue). The only way to store multiple values for a particular key in a java standard Map is to store "collection" as value, then you need to access this collection (from Map) by key, and then use this collection "value" as regular collection, in your example as ArrayList. So you do not put something directly by map.put (except from creating the empty collection), instead you take the whole collection by key and use this collection.
You need something like Multimap, for example:
public class Multimap<T,S> {
Map<T, ArrayList<S>> map2 = new HashMap<T, ArrayList<S>>();
public void add(T key, S value) {
ArrayList<T> currentValuesForGivenKey = get(key);
if (currentValuesForGivenKey == null) {
currentValuesForGivenKey = new ArrayList<T>();
map2.get(key, currentValuesForGivenKey);
}
currentValuesForGivenKey.add(value);
}
public ArrayList<S> get(T key) {
ArrayList<String> currentValuesForGivenKey = map2.get(key);
if (currentValuesForGivenKey == null) {
currentValuesForGivenKey = new ArrayList<S>();
map2.get(key, currentValuesForGivenKey);
}
return currentValuesForGivenKey;
}
}
then you can use it like this:
Multimap<String,String> map2 = new Multimap<String,String>();
map2.add("1","2");
map2.add("1","3");
map2.add("1","4");
for (String value: map2.get("1")) {
System.out.println(value);
}
will print:
2
3
4
it gives error that says "incompatible types" and it can not be converted to ArrayList and it says the error is at the value part of the line.
because, it won't automatically convert to ArrayList.
You should add both the values to list and then put that list in map.

Hashmap's getValue returns Object

I've got the following data structure:
CFU66=[{Bild1=CFU6606}, {Bild2=CFU6603}, {Bild3=CFU6605}, {Bild4=CFU6601}, {Bild5=CFU6602}]
Structure: Hashmap_1(String Key, List(Hashmap_2(String Key, String Value)))
I'm trying to access the values from Hashmap_2:
// for each Hashmap_1 entry
for (Map.Entry<String, List> csvDictEntry : csvDict.entrySet()) {
// for each List in entry.getValue
for (List<HashMap> hashList : csvDictEntry.getValue()) {
// for each Hashmap_2 in List
for (HashMap<String, String> hashListDict : hashList) {
// for each entry in Hashmap_2 print Value
for (Map.Entry<String, String> entry :hashListDict.entrySet()){
System.out.println(entry.getValue());
}
}
}
}
The compiler gives the message, that csvDictEntry.getValue() in the second for-loop returns a Object instead of a Hashmap. Why?
However, I'm pretty new to Java and I'm sure there is a more convenient way to do this.
this
for (Map.Entry<String, List> csvDictEntry : csvDict.entrySet()) {
should be
for (Map.Entry<String, List<Map<String, String>>> csvDictEntry : csvDict.entrySet()) {
Just write all your types
Map<String, List<HashMap<String, String>>> csvDict = null;
for (Map.Entry<String, List<HashMap<String, String>>> csvDictEntry : csvDict.entrySet()) {
// for each List in entry.getValue
for (HashMap<String, String> hashList : csvDictEntry.getValue()) {
// for each Hashmap_2 in List
for (Map.Entry<String, String> entry : hashList.entrySet()) {
System.out.println(entry.getValue());
}
}
}
And also you have extra for loop.
Based on
for (Map.Entry<String, List> csvDictEntry : csvDict.entrySet()) {
I assume that type of csvDict is
Map<String, List> csvDict = ...;
where based on your data example CFU66=[{Bild1=CFU6606}, {Bild2=CFU6603}, {Bild3=CFU6605}, {Bild4=CFU6601}, {Bild5=CFU6602}]
it should be
Map<String, List<Map<String,String>>> csvDict = ...;
Problem with your reference type is that List is raw-type, which means that its actual type is unknown (it can store any kind of Objects) so Object is type which compiler assumes when you are trying to iterate over such list so when normally we would expect
for (Type t : List<Type>)
for raw type we are getting
for (Object o : rawList)
Other problem is way you are iterating because even if we change your reference to proper type
for (List<HashMap> hashList : csvDictEntry.getValue())
will not compile because getValue() returns List<HashMap> so your loop would iterate over HashMaps, not List of HashMaps so it should be
for (HashMap hashList : csvDictEntry.getValue())
Hint: try to avoid concrete types in generics, use parent type if it is possible, like interface or abstract type. This will allow you later easily changing actual type, for instance from HashMap to LinkedHashMap.
So your iteration should look like
for (Map.Entry<String, List<Map<String, String>>> csvDictEntry : csvDict.entrySet()) {
// for each Map in List stored as value
for (Map<String, String> hashListDict : csvDictEntry.getValue()) {
// for each entry in hmap_2 print Value
for (Map.Entry<String, String> entry : hashListDict.entrySet()) {
System.out.println(entry.getValue());
}
}
}
DEMO
BTW in Java 8 your code could be simplified to
csvDict.entrySet().stream()
.flatMap(m -> m.getValue().stream())
.flatMap(m -> m.values().stream())
.forEach(System.out::println);
As indicated by others you forgot to use <> after 'List' on your first line of code. Therefore, the compiler doesn't know what kind of elements are in those lists.
Also you're making it unnecessarily complex by iterating over entrySet when you're only interested in the values.
Map has 3 functions to iterate:
keySet() - if you're only interested in the keys
values() - if you're only interested in the values
entrySet() - if you're interested in both
So in your case...
for (Map<String,String> map : csvDict.values()) {
for (String value : map.values()) {
System.out.println(value);
}
}
Your problem is the first for,what you are currently doing is retrieving a generic list,instead of a particular list.
The declaration of entry set is:
Set<Map.Entry<K,V>> <------- entrySet()
Returns a Set view of the mappings contained in this map.
so you should reconsider chaging your first for to:
for (Map.Entry<String, List<HashMap>> csvDictEntry : csvDict.entrySet()) {

Issue to get data from a collection

I am trying to get data out of a list,but facing some issue while getting the data.
List<Field> errorFieldList;
Set<String> formValidationResult = new HashSet<String>();
Here the data added to validationResults is like and errorFieldList size is two having Id and type
validationResults.put(errorFieldList, formValidationResult);
public ValidationResponseErrorView(
Map<Object, Set<String>> validationResults, String exceptionMessage) {
if (validationResults.size() > 0) {
for (Map.Entry<Object, Set<String>> entrySet : validationResults
.entrySet()) {
Map<String, Object> fieldResultsMap = new HashMap<String, Object>();
Object objField = entrySet.getKey();
if (objField instanceof List) {
for (int i = 0; i < ((List<Map<String, Object>>) objField)
.size(); i++) {
LOGGER.info("in array list----" + objField);
}
}
}
}
}
I am not sure how to get data out of objField.
Your code is really hard to read and contains a lot of inconsistencies. Especially in your constructor you declare validationResults as
Map<Object, Set<String>> validationResults
But in your introduction you declare it as
List<Field> errorFieldList;
Set<String> formValidationResult = new HashSet<String>();
validationResults.put(errorFieldList, formValidationResult);
which means your Object is a List<Field>. So now you can simply use:
objField.get(i);
to retrieve the values, where i is your index you iterate over. HOWEVER: your code probably won't compile, since you try to cast the List<Field> to List<Map<String, Object>> in the 2nd for loop.
Simply put: rework the code, A Map has getter methods for the key and over a Set you need to iterate. Check first what data structure you need. Don't nest too deep.
List< Map< String, Object>> objField would return you a List which has elements of type Map<String, Object>. To get value from this use:
Map<String,Object> mapOut = ((List<Map<String, Object>>) objField).get(i);
Set<String> keySet = mapOut.keySet();
Iterator<String> itr = keySet.iterator();
while(itr.hasNext()){
Object outObject = mapOut.get(itr.next());
LOGGER.info("Object"+ outObject);
}

Categories