I have a generic map
Map<String, List<A<?>>> map = new HashMap<>();
then I have a list like:
List<A<Integer>> list1 = getData();
List<A<String>> list2 = getData2();
I'm trying to add them to the map, but I'm getting an error saying the argument is wrong.
map.put("a", list1);
map.put("b", list2);
//doesn't work
Making the map like
Map<String, List<A>> map = HashMap<>();
doesn't work either.
How can I change "map" to be able to add those 2 lists?
Generics are not evaluated recursively. The wildcard (<?>) does not get evaluated here.
If you have a Map<String, List<A<?>>> then you must add List<A<?>> values to it; List<A<Integer>> does not qualify as a List<A<?>>. List<A<?>> means a List of A instances with unknown types, so you must pass a List of A instances with unknown types.
What you can do is create such a List, explicitly, and add all the elements of your typed-A List to it:
List<A<Integer>> list1 = getData();
List<A<String>> list2 = getData2();
List<A<?>> list1Unknown = new ArrayList<>();
list1.forEach(list1Unknown::add);
List<A<?>> list2Unknown = new ArrayList<>();
list2.forEach(list2Unknown::add);
map.put("a", list1Unknown);
map.put("b", list2Unknown);
If you just want to place any two lists inside the map then use: Map<String, List> map = new HashMap<>();.
Alternatively, if you want to restrict the type of the List that you would like to add:
Map<String, List<? extends Object>> map = new HashMap<>();.
Why I have chosen to extend Object is because it is the only class present in the hierarchies of both Integer and String.
Related
I have a collection:
Collection<Map<String, Object>> items = new ArrayList();
Map<String, Object> item1 = new HashMap();
Map<String, Object> item2 = new HashMap();
item1.put("first_name", "john");
item1.put("last_name", "doe");
item2.put("first_name", "jane");
item2.put("last_name", "doe");
items.add(item1);
items.add(item2);
I would like to filter out using stream all maps in collection that have "first_name" set as "jane". And it should return a collection of maps (same type).
If it could be done by not using stream I don't mind but I'd prefer stream.
Not too hard to do. Just add this to your code.
Collection<Map<String, Object>> items2 = items.stream().filter(a -> !"jane".equals(a.get("first_name"))).collect(Collectors.toList());
You do need to specify what happens if the map doesn't contain the key "first_name" at all, I'm assuming that it needs to have a first_name key (that isn't jane) to be valid post-filter.
Additionally, just a heads up. Your existing code isn't using generics correctly; my IDE at least gave me a warning of unchecked casting on your first three lines. After Java 1.7 you can have it infer generic type arguments, but you still need to supply the symbols <> to make that happen. Here's a fixed version:
Collection<Map<String, Object>> items = new ArrayList<>();
Map<String, Object> item1 = new HashMap<>();
Map<String, Object> item2 = new HashMap<>();
ArrayList<Object> parameters = new ArrayList<Object>();
HashMap<String, String> parameter = new HashMap<String, String>();
parameter.put("key", "value");
parameters.add(parameter);
parameters.add((String) "additionalData"); //this line is here for a reason
destinationFunction(parameters);
....
destinationFunction(ArrayList<Object> data){
HashMap<String, String> imported = (HashMap<String, String>) data.get(0);
String value = imported.get("key");
}
How do i achieve this? When i try i receive no errors up until like 2 of destinationFunction where i receive null pointer exception
ArrayList<Object> parameters = new ArrayList<Object>();
relpace this line with what you want to store in array list
like you want to store hash map then create arraylist of type hash map
ArrayList<HashMap<String, String>> parameters = new ArrayList<HashMap<String, String>>();
hope this will help you
Alternative Solution would be using JsonObject and JsonArrays.
they are perfect for such "scenarios" (working with String,Integer and etc with combination of List and Map).
they may also be useful for complex object(e.g. working with Images) but will be abit more difficult to implement. in such cases please refer to java_serialization
in your case use JSONArray instead of your ArrayList and JsonObject instead of HashMap.
use array list of hashmap
ArrayList<HashMap<String, String>>()
Try to put ArrayList<HashMap<String, String>> instead of ArrayList<Object>
create a HashMap and add into the ArrayList.
For Example:-
ArrayList<HashMap<String, String>> list = new ArrayList<>();
HashMap<String, String> map = new HashMap<>();
map.put("KEY", "VALUE");
list.add(map); //Add the map into list
You did not specified what type are you storing in your arrayList. But you assume in your destinationFunction that this list contains only HashMap<String, String> elements. Besides terrible code style it is okay in java to write so. But you made a mistake when put in your arrayList an element of type String which is breaking your assumptions in destinationFunction.
P.S. Try to write more type safe code and avoid generic types with Object type parameter.
Is there any difference between the following declarations -
List<String> list = new ArrayList<String>();
and
List<String> list = new ArrayList<>();
In both cases anyhow , list will have elements of type String only.
There is no difference. However, the first one is legal in Java <= 7 whereas the second one is legal only in Java 7 and was introduced as a short-hand notation*. The compiler will infer the generic type from the declaration.
*It was basically introduced to remove redundant information and reduce code-noise. So you now have:
Map<String, List<String>> myMap = new HashMap<>();
versus:
Map<String, List<String>> myMap = new HashMap<String, List<String>>();
The first one is a lot easier on the eyes.
I have made two Lists like
List<LearnerEnrollment> learnerEnrollmentList = new ArrayList<LearnerEnrollment>();
List<LearnerCourseEnrollError> enrollErrorList = new ArrayList<LearnerCourseEnrollError>();
Then i made two Maps like
Map<String, List<LearnerCourseEnrollError>> courseErrorMap = new HashMap<String, List<LearnerCourseEnrollError>>();
Map<String, List<LearnerEnrollment>> courseSuccessMap = new HashMap<String, List<LearnerEnrollment>>();
Then i made another Map to hold the above two Maps like
Map<String, Map<String, List<Object>>> courseMap = new HashMap<String, Map<String, List<Object>>>();
Then i use the following code to add items in lists;
for (com.softech.vu360.lms.model.Course course : courseList) {
Object result = getEnrollmentForCourse(customer, learner, course);
if (result instanceof LearnerEnrollment) {
LearnerEnrollment newEnrollment = (LearnerEnrollment)result;
learnerEnrollmentList.add(newEnrollment);
} else if (result instanceof String) {
String errorMessage = (String)result;
LearnerCourseEnrollError enrollError = new LearnerCourseEnrollError(errorMessage, course);
enrollErrorList.add(enrollError);
}
}
Now i am putting values in the Map
courseSuccessMap.put(learner.getVu360User().getUsername(), learnerEnrollmentList);
courseErrorMap.put(learner.getVu360User().getUsername(), enrollErrorList);
courseMap.put("successfulCoursesMap", courseSuccessMap);
courseMap.put("unSuccessfulCoursesMap", courseErrorMap);
return courseMap;
But i am getting error at these two lines
courseMap.put("successfulCoursesMap", courseSuccessMap);
courseMap.put("unSuccessfulCoursesMap", courseErrorMap);
that
The method put(String, Map<String,List<Object>>) in the type
Map<String,Map<String,List<Object>>> is not applicable for the arguments
(String, Map<String,List<LearnerEnrollment>>)
The method put(String, Map<String,List<Object>>) in the type
Map<String,Map<String,List<Object>>> is not applicable for the arguments
(String, Map<String,List<LearnerCourseEnrollError>>)
Why?
My list type in the Map is List<Object> and List<LearnerEnrollment> is List <Object> because LearnerEnrollment extends Object. Why I am getting these errors ?
If i declare my Map like this
Map<String, Map<String, ?>> courseMap = new HashMap<String, Map<String, ?>>();
Then there is no error. Why i am getting error in first case?
Thanks
You said:
List<LearnerEnrollment> is List<Object>
This is wrong. If it were true, you would be able to do:
List<<LearnerEnrollment> list = new ArrayList<>();
List<Object> objectList = list;
objectList.add("Now what?");
And your type-safe list of LearnerEnrollment would suddenly contain a String.
This is because a List in java is not covariant. A List<LearnerEnrollment> is not a subclass of List<Object>.
See Java covariance for more information.
I need to use a Map with a List inside :
Map<String, List<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
I am getting compiler error on this line in eclipse.
The only working thing seem to be changing the inside List in the Map to ArrayList
Map<String, ArrayList<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
I had to change the signature of many interfaces' methods, but I still don't get it; why isn't the first definition work?
Isn't it the same, should not
Map<String, List<String>> keyToGroup
&
Map<String, ArrayList<String>>
be the same?
No, they're not. Consider this:
Map<String, List<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
keyToGroup.put("foo", new LinkedList<String>());
The second line is fine, because a LinkedList<String> is a List<String> - but it's not logically fine in terms of adding it to a HashMap<String, ArrayList<String>>, because a LinkedList<String> is not an ArrayList<String>.
To make it clearer:
Map<String, ArrayList<String>> map1 = new HashMap<String, ArrayList<String>>();
Map<String, List<String>> map2 = map1; // This is invalid
map2.put("foo", new LinkedList<String>());
ArrayList<String> oops = map1.get("foo"); // Because this would be broken
This isn't just the case with collections as the type argument. It's even simpler to see with normal inheritance:
List<Banana> bunchOfBananas = new ArrayList<Banana>();
List<Fruit> fruitBowl = bunchOfBananas; // Invalid!
fruitBowl.add(new Apple());
Banana banana = bunchOfBananas.get(0);
Even though every banana is a fruit, so a "collection of bananas" is a "collection of fruit* in the sense of fetching them, not every fruit is a banana.
You can use wildcard parameterized types to help in some cases, but it depends on exactly what you're trying to achieve.
Ask yourself a question if you need particular list implementation in your Map or any List?
In case of particular implementation you can use your last example:
Map<String, ArrayList<String>> keyToGroup = new HashMap<String, ArrayList<String>>();
In case of any list just use:
Map<String, List<String>> keyToGroup = new HashMap<String, List<String>>();
keyToGroup.put("arraylist", new ArrayList<String());
keyToGroup.put("linkedlist", new LinkedList<String());
BTW the second option usually is better from design point of view so if you don't know exactly for now - try using second option first.
No they are not. Generics are not covariant in Java.
If they are covariant you can logically put any type of List instead of ArrayList which defeats the purpose of having generics.
Consider reading Effective Java (2nd Edition) Chapter 5: Generics which has very good explanation of Generics.
Another good read is http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html