"Incompatible types" when accessing Map#entrySet() - java

I am working on a simple config file reader for fun, but I am getting an odd error while writing the test method. In it is a for loop, and I have made sure that it causes the problem. It gives me this compilation error:
Incompatible types:
Required: java.util.Map.Entry
Found: java.lang.Object
The Map declaration is this:
Map<String, String> props = new HashMap<String, String>();
The for loop is written as below:
for (Map.Entry<String, String> entry : props.entrySet()) {
//Body
}
An SSCCE without imports which demonstrates the problem (At least in IntelliJ):
public class A {
public static void main(String[] args) {
Map<String, String> props = new HashMap<String, String>();
for (int i = 0; i < 100; i++) {
props.put(new BigInteger(130, random).toString(32), new BigInteger(130, random).toString(32));
}
for (Map.Entry<String, String> entry : props.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
}
map is a Map<String, String>, so that can't be the problem. I have googled alternative methods of doing this, but the main one people use seems to be this one! Yet for some reason it still fails. Any help would be appreciated. If you offer an alternative solution, please make sure it is fast - these config files could potentially be huge.

Here's a demonstration of what you may be doing - it is difficult to be sure without more code.
class ATest<T> {
Map<String, String> props = new HashMap<String, String>();
void aTest() {
// Works fine.
for (Map.Entry<String, String> entry : props.entrySet()) {
}
}
void bTest() {
ATest aTest = new ATest();
// ERROR! incompatible types: Object cannot be converted to Entry<String,String>
for (Map.Entry<String, String> entry : aTest.props.entrySet()) {
}
}
void cTest(Map props) {
// ERROR! incompatible types: Object cannot be converted to Entry<String,String>
for (Map.Entry<String, String> entry : props.entrySet()) {
}
}
}
Notice that in bTest I create an ATest without its generic type parameter. In that situation Java removes all generic information from the class, including, as you will see, the <String,String> from the props variable inside it.
Alternatively - you may be accidentally removing the generic nature of the properties map - like I demonstrate in cTest.

Try to declare your map as a HashMap.
HashMap<String, String> props = new HashMap<String, String>();
This is what solved my problem.

Related

why HashMap<String, Object> not accept HashMap<String, List> instance?

I am new in java generics and facing following issues.
I have have method like,
private static void fillDescriptiveData(HashMap<String, Object> output, String attributeMapping) {
for (Map.Entry<String, Object> outputInEntry : output.entrySet()) {
String outputKey = outputInEntry.getKey();
String outputValue = outputInEntry.getValue().toString();
outputValue = getDescriptiveDataForOutput(outputKey, outputValue, attributeMapping);
outputInEntry.setValue(outputValue);
}
}
Now if I call API as below way
HashMap<String, Object> ObjectMap = new HashMap<String, Object>();
HashMap<String, List> listMap = new HashMap<String, List>();
fillDescriptiveData(ObjectMap,"here");
this one working fine.
fillDescriptiveData(listMap,"here");
this call gives error
The method fillDescriptiveData(HashMap, String) in the type CustomAttribute is not applicable for the arguments (HashMap, String)`
why ?
In row to solve this issue I encounter with one more issue,
private static void fillDescriptiveData(HashMap<String, ? extends Object> output, String attributeMapping) {
for (Map.Entry<String, ? extends Object> outputInEntry : output.entrySet()) {
String outputKey = outputInEntry.getKey();
String outputValue = outputInEntry.getValue().toString();
outputValue = getDescriptiveDataForOutput(outputKey, outputValue, attributeMapping);
outputInEntry.setValue(outputValue); /* Error comes at this line */
}
}
HashMap<String, ? extends Object> ObjectMap = new HashMap<String, Object>();
HashMap<String, List> listMap = new HashMap<String, List>();
fillDescriptiveData(ObjectMap,"here");
fillDescriptiveData(listMap,"here");
error at line - outputInEntry.setValue(outputValue);
The method setValue(capture#4-of ? extends Object) in the type
Map.Entry is not applicable for
the arguments (String)
why ?
What is the best way to avoid this issues ?
This is the case when you could use type variables:
private static <T> void fillDescriptiveData(Map<String, T> output,String attributeMapping)
{
for(Map.Entry<String, T> outputInEntry : output.entrySet())
{
String outputKey = outputInEntry.getKey();
String outputValue = outputInEntry.getValue().toString();
outputValue = getDescriptiveDataForOutput(outputKey, outputValue, attributeMapping);
outputInEntry.setValue((T) outputValue);
}
}
More specifically, your second type-parameter in the map is unbounded. Object will not work here as it is specific class. ? extends Object is somewhat nonsense.
Just HashMap<String, ?> would work until you will just read the map, but you will not be able to put something here. So only one way - using type variable.
EDIT: One more thing: please, use interfaces where it's possible. So here instead of HashMap<String, T> better use Map<String, T>. It isn't a mistake, just good and proper style of code.
The error with this line:
outputInEntry.setValue(outputValue);
Is that you're always putting a string into the entry. This will only work if the entry is of type ? super String, or exactly String. So it will not work for a Map<String, Object> or Map<String, List>.
It seems like you just want to map each value to a string. You can do it, but to be type safe, you need to create a new Map<String, String>. Since you're always mapping to a String.
If you for instance pass in a Map<String, List<?>> and (unsafely) replace all the values with strings. Someone could still keep using the Map<String, List<?>> that was passed into the function, but it now contains strings as values instead of lists. When they try to retrieve a List from it they get a class cast exception.
Something like this:
private static Map<String, String> fillDescriptiveData(HashMap<String, ?> input,
String attributeMapping) {
Map<String, String> output = new HashMap<>();
for(Entry<String, ?> e : input.entrySet()) {
String outputKey = e.getKey();
String outputValue = e.getValue().toString();
outputValue
= getDescriptiveDataForOutput(outputKey, outputValue, attributeMapping);
output.put(outputKey, outputValue);
}
return output;
}
Map<String, String> r1 = fillDescriptiveData(ObjectMap, "here");
Map<String, String> r2 = fillDescriptiveData(listMap, "here");

Odd behavior with Maps

I am getting a syntax error which I am not able to resolve. I am using Java 1.8.
import java.util.*;
public class datatypetest
{
public static void main(String args[])
{
Map map1 = new HashMap();
map1.put("1", "Deepak");
map1.put("2", "Ajay");
System.out.println(map1);
System.out.println(map1.keySet());
for (Map.Entry<String, String> entry : map1.entrySet())
{
System.out.println(entry.getKey() + "/" + entry.getValue());
}
}
}
But I am getting this error:
incompatible types: Object can not be converted to Entry<String,String>
You created a raw map :
Map map1 = new HashMap();
Change it to:
Map<String,String> map1 = new HashMap<String,String>();
If you instantiate the map as a raw Map, you can't use Map.Entry<String, String> in the loop (you can only use the raw Map.Entry).
You need to use Generics to avoid such Type of Conflicts i.e
Map<String, String> map1 = new HashMap<String, String>();
Generics provides Type Safety.
And in addition I've found in your code that your Class name didn't follow best practices. It indeed must start with Capital letter since it's a best practice entire JAVA world follows
Try This
import java.util.*;
public class DataTypeTest {
public static void main(String args[]){
Map<String, String> map1 = new HashMap<String, String>();
map1.put("1", "Deepak");
map1.put("2", "Ajay");
System.out.println(map1);
System.out.println(map1.keySet());
for (Map.Entry<String, String> entry : map1.entrySet())
{
System.out.println(entry.getKey() + "/" + entry.getValue());
}
}
}
Happy Programming :)
because you have created
Map map1 = new HashMap();
can be of any type(not just string) so java is not allowing you to downcast it.

Easily convert Map<String, Object> to Map<String, String>

An API I am using has a method that returns a Map<String, Object>, but I know the Object's are String's in this case, so I want it as a Map<String, String>.
But for some reason I can't just cast it, Java says Map<String, Object> cannot be casted to Map<String, String>, for some reason.
I used:
Map<String, Object> tempMap = someApiMethodReturningAMap();
Map<String, String> map = new HashMap<String, String>();
for (String i : tempMap.keySet()) {
map.put(i, String.valueOf(tempMap.get(i)));
}
as a workaround, but is there an easier way?
Well you can't safely cast it to a Map<String, String> because even though you know you've only got strings as the values, the compiler doesn't. That's like expecting:
Object x = "foo";
String y = x;
to work - it doesn't; you need to explicitly cast.
Likewise you can explicitly cast here, too, if you go via Object:
Map<String, Object> x = ...;
Map<String, String> y = (Map<String, String>) (Object) x;
Now you'll get a warning saying that it's an unchecked cast, because unlike the earlier "object to string" cast, there's no execution-time check that it's really valid. Type erasure means that a map doesn't really know its key/value types. So you end up with checking only being done when elements are fetched:
import java.util.*;
class Test {
public static void main(String[] args) {
Map<String, Object> x = new HashMap<>();
x.put("foo", "bar");
x.put("number", 0);
Map<String, String> y = (Map<String, String>) (Object) x;
// This is fine
System.out.println(y.get("foo"));
// This goes bang! It's trying to cast an Integer to a String
System.out.println(y.get("number"));
}
}
So if you really want to avoid creating a new map, this "cast via Object" will work - but it's far from ideal.
Your approach is safer, although you can make it slightly more efficient by avoiding the lookup:
public static Map<String, String> copyToStringValueMap(
Map<String, Object> input) {
Map<String, String> ret = new HashMap<>();
for (Map.Entry<String, Object> entry : input.entrySet()) {
ret.put(entry.getKey(), (String) entry.getValue());
}
return ret;
}
A Java 8 solution:
private Map<String, String> stringifyValues(Map<String, Object> variables) {
return variables.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> (String) e.getValue()));
}
Good solutions here, but I want to add another one that taking into consideration handling null values:
Map<String,Object> map = new HashMap<>();
Map<String,String> stringifiedMap = map.entrySet().stream()
.filter(m -> m.getKey() != null && m.getValue() !=null)
.collect(Collectors.toMap(Map.Entry::getKey, e -> (String)e.getValue()));

Get values of Map from a Map

In my code i have a
Map<String,Map<String,customObject>>
I am not sure how to iterate over this map and get the values from it.
What i am trying to do here is get the enclosing Map by passing in the key to the external Map.
When i get the enclosing map i need to iterate over it and get key and value from it.
Can you please let me know how i can do this as i am kind of stuck here.
Any example or code of a similar type can be of a great help to understand it better.
Thanks
Vikeng21
You can use the entry set of both Maps. something like this:
Map<String,Map<String,String>> map1 = ...
Set<Entry<String,Map<String,customObject>>> entrySet1 = map1.entrySet();
for (Entry<String, Map<String, customObject>> entry1 : entrySet1) {
Map<String,String> map2 = entry1.getValue();
Set<Entry<String, customObject>> entrySet2 = map2.entrySet();
for (Entry<String, customObject> entry2 : entrySet2) {
System.out.println(entry1.getKey() +" -> "+entry2.getKey()+" -> "+entry2.getValue());
}
}
To iterate over hashmap entries...
for (Map.Entry<String, Map<String, Object>> ent : hashmap.entrySet())
{
//ent.getKey(); is the key [String]
//ent.getValue(); is the value [Map<String, Object>]
}
Now work out from there, it's basically the same.
I am not sure how to iterate over this map and get the values from it
You would iterate over the map's values like with any maps - see below an example that uses such a structure.
Map<String, CustomObject> innerMap = new HashMap<String, CustomObject> ();
innerMap.put("abc", new CustomObject());
Map<String, Map<String, CustomObject>> externalMap = new HashMap<String, Map<String, CustomObject>> ();
externalMap.put("map1", innerMap);
//iterate over all the maps contained in externalMap
for (Map<String, CustomObject> inner : externalMap.values()) {
System.out.println(inner);
}
If you also need to access the keys, you can iterate over the entry set:
for (Entry<String, Map<String, CustomObject>> e : externalMap.entrySet()) {
System.out.println(e.getKey()); //map1
System.out.println(e.getValue()); //innerMap
}
I think this example will give your answer....
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class MapInMap {
public static void main(String[] args) {
Map<String, MyObj> innerMap01 = new HashMap<String, MyObj>();
Map<String, MyObj> innerMap02 = new HashMap<String, MyObj>();
innerMap01.put("OneOne", new MyObj());
innerMap02.put("TwoOne", new MyObj());
Map<String, Map<String, MyObj>> maps = new HashMap<String, Map<String, MyObj>>();
maps.put("One", innerMap01);
maps.put("Two", innerMap02);
for (Entry<String, Map<String, MyObj>> map : maps.entrySet()) {
for (Entry<String, MyObj> innerObject : map.getValue().entrySet()) {
// your logic
}
}
}
}
class MyObj {
int i;
}

Pass an object as a parameter and modify it within the method

Let's say I got a Map<String, String> and I wanna remove all the entries that value contains foo. What is the best way to do it, in terms of optimization/memory/etc.? The four syso below are printing the same result, that is to say {n2=bar}.
public static void main(String[] args) {
Map<String, String> in = new HashMap<String, String>();
in.put("n1", "foo");
in.put("n2", "bar");
in.put("n3", "foobar");
// 1- create a new object with the returned Map
Map<String, String> in1 = new HashMap<String, String>(in);
Map<String, String> out1 = methodThatReturns(in1);
System.out.println(out1);
// 2- overwrite the initial Map with the returned one
Map<String, String> in2 = new HashMap<String, String>(in);
in2 = methodThatReturns(in2);
System.out.println(in2);
// 3- use the clear/putAll methods
Map<String, String> in3 = new HashMap<String, String>(in);
methodThatClearsAndReadds(in3);
System.out.println(in3);
// 4- use an iterator to remove elements
Map<String, String> in4 = new HashMap<String, String>(in);
methodThatRemoves(in4);
System.out.println(in4);
}
public static Map<String, String> methodThatReturns(Map<String, String> in) {
Map<String, String> out = new HashMap<String, String>();
for(Entry<String, String> entry : in.entrySet()) {
if(!entry.getValue().contains("foo")) {
out.put(entry.getKey(), entry.getValue());
}
}
return out;
}
public static void methodThatClearsAndReadds(Map<String, String> in) {
Map<String, String> out = new HashMap<String, String>();
for(Entry<String, String> entry : in.entrySet()) {
if(!entry.getValue().contains("foo")) {
out.put(entry.getKey(), entry.getValue());
}
}
in.clear();
in.putAll(out);
}
public static void methodThatRemoves(Map<String, String> in) {
for(Iterator<Entry<String, String>> it = in.entrySet().iterator(); it.hasNext();) {
if(it.next().getValue().contains("foo")) {
it.remove();
}
}
}
The best way is methodThatRemoves because:
In terms of memory consumption: it doesn't create a new map so doesn't add memory overhead.
In terms of CPU use: iterator has O(1) complexity for calling next or removing the current element.
The most efficient way methodThatRemoves, because it
Uses almost no memory
Creates no objects except the (lightweight) iterator
Is extremely fast (doesn't use any map lookups)
I would not make a copy first though, unless you have an unmodifiable map or you need to preserve the original.
For me the best is the one with the Iterator - methodThatRemoves, because you don't create an intermediate Map and don't use put method.
By the way the first one : methodThatReturns can be faster because put complexity is O(1) whereas remove is O(n) in worst case but it will use more memory because you have 2 different instances of Map.
I would personally go with methodThatRemoves because you are only performing a loop operation and checking for "foo" equality. The others do that as well as Object map creation and map clear/put operations. So you clearly have 1 method doing less.
Also if you want to reduce memory usage you are better off not creating an extra HashMap just to remove 1 or more entries. This is assuming you don't mind the extra computation to iterate the map.
If you really want to go more in depth, you should evaluate this using a profiler or some sort.

Categories

Resources