I have a situation where I need to copy my EnumMap<ExampleEnum,String> to Map<String, Object>. Many examples on Stack Overflow shows how to cast from one data type to another but not from enum. I have tried doing it through stream but no luck. Here is my code
private enum Number{
One, Two, Three;
}
final Map<Number, String> map = Collections.synchronizedMap(new EnumMap<Number, String> (Number.class));
populateMap(map);
Map<String, Object> newMap= new HashMap<String, Object>();
Now I want to do something like
newMap.putAll(map);
How can I do it through Stream APIs?
A more concise answer is,
final Map<Number, String> map = Collections.synchronizedMap(new EnumMap<>(Number.class));
Map<String, Object> newMap= new HashMap<>();
map.forEach((key, value) -> newMap.put(key.name(), value));
Map<String, Object> newMap = map.entrySet().stream()
.collect(Collectors.toMap(e -> e.getKey().toString(), Map.Entry::getValue));
Related
I want to convert:
Map<String, Map<String, List<Map<String, String>>>> inputMap
to:
Map<String, Map<String, CustomObject>> customMap
inputMap is provided in the config and is ready but I need to customMap Format. CustomObject will be derived from List<Map<String, String>> using few lines of code in a function.
I have tried a normal way of iterating input map and copying key values in customMap. Is there any efficient way of doing that using Java 8 or some other shortcut?
Map<String, Map<String, List<Map<String, String>>>> configuredMap = new HashMap<>();
Map<String, Map<String, CustomObj>> finalMap = new HashMap<>();
for (Map.Entry<String, Map<String, List<Map<String, String>>>> attributeEntry : configuredMap.entrySet()) {
Map<String, CustomObj> innerMap = new HashMap<>();
for (Map.Entry<String, List<Map<String, String>>> valueEntry : attributeEntry.getValue().entrySet()) {
innerMap.put(valueEntry.getKey(), getCustomeObj(valueEntry.getValue()));
}
finalMap.put(attributeEntry.getKey(), innerMap);
}
private CustomObj getCustomeObj(List<Map<String, String>> list) {
return new CustomObj();
}
One solution is to stream the entrySet of inputMap, and then use Collectors#toMap twice (once for the outer Map, and once for the inner Map):
Map<String, Map<String, CustomObj>> customMap = inputMap.entrySet()
.stream()
.collect(Collectors.toMap(Function.identity(), entry -> {
return entry.getValue()
.entrySet()
.stream()
.collect(Collectors.toMap(Function.identity(),
entry -> getCustomeObj(entry.getValue())));
}));
You could stream, but that ain't going to look readable; at least to me. So if you have a method:
static CustomObject fun(List<Map<String, String>> in) {
return .... // whatever processing you have here
}
you could still use the java-8 syntax, but in a different form:
Map<String, Map<String, CustomObject>> customMap = new HashMap<>();
inputMap.forEach((key, value) -> {
value.forEach((innerKey, listOfMaps) -> {
Map<String, CustomObject> innerMap = new HashMap<>();
innerMap.put(innerKey, fun(listOfMaps));
customMap.put(key, innerMap);
});
});
If you can make the inner map immutable, you could make that even shorter:
inputMap.forEach((key, value) -> {
value.forEach((innerKey, listOfMaps) -> {
customMap.put(key, Collections.singletonMap(innerKey, fun(listOfMaps)));
});
});
IMHO streaming is not so bad idea. There're no bad tools. It depends on how you're using them.
In this particular case I would extract the repeating pattern into an utility method:
public static <K, V1, V2> Map<K, V2> transformValues(Map<K, V1> map, Function<V1, V2> transformer) {
return map.entrySet()
.stream()
.collect(toMap(Entry::getKey, e -> transformer.apply(e.getValue())));
}
The method above can be implemented using any approach, though I think Stream API fits pretty well here.
Once you defined the utility method, it can be used as simple as follows:
Map<String, Map<String, CustomObj>> customMap =
transformValues(inputMap, attr -> transformValues(attr, this::getCustomObj));
The actual transformation is effectively one liner. So with proper JavaDoc for transformValues method the result code is pretty readable and maintainable.
How about Collectors.toMap for the entries both at an outer and inner level such as:
Map<String, Map<String, CustomObj>> finalMap = configuredMap.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey,
attributeEntry -> attributeEntry.getValue().entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey,
valueEntry -> getCustomeObj(valueEntry.getValue())))));
I have following class:
public class Foo {
private String areaName;
private String objectName;
private String lineName;
}
Now I want to convert a List<Foo> to Map<String, Map<String, List<String>>>. I found this answer which helped me develop following code:
Map<String, Map<String, List<String>>> testMap = foos.stream().collect(Collectors.groupingBy(e -> e.getAreaName(),
Collectors.groupingBy(e -> e.getObjectName(), Collectors.collectingAndThen(Collectors.toList(),
e -> e.stream().map(f -> f.getLineName())))));
The only problem with this code is the part where it should convert to List<String>. I couldn't find a way to convert Foo to List in that part.
Another approach which results in Map<String, Map<String, List<Foo>>>:
Map<Object, Map<Object, List<Foo>>> testMap = ventures.stream().collect(Collectors.groupingBy(e -> e.getAreaName(),
Collectors.groupingBy(e -> e.getObjectName(), Collectors.toList())));
What do I need to change in order to receive Map<String, Map<String, List<String>>> from List<Foo>?
In order to get a List of a different type than the type of the Stream elements, you should chain a Collectors.mapping collector to groupingBy:
Map<String, Map<String, List<String>>> testMap =
foos.stream()
.collect(Collectors.groupingBy(Foo::getAreaName,
Collectors.groupingBy(Foo::getObjectName,
Collectors.mapping(Foo::getLineName,
Collectors.toList()))));
I have Map<String, Object> which has to be become Map<String, String>. Filtering should be done by List<String>.
That list contains keys of map elements that should be in new map.
For this I need to use streams.
Map<String, Object> oldMap;
List<String> keysForFiltering;
Map<String, String> newMap;
It would be more efficient if the filter would operate on a Set of keys instead of a List of keys, since searching a Set is more efficient than searching a List.
Map<String, String> newMap =
oldMap.entrySet()
.stream()
.filter(e -> keysForFiltering.contains(e.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey,
e -> e.getValue().toString()));
since you have a map then you can get the stream of that and use a custom predicate, that predicate need to check if the Entry.key is present in your list or not
Map<String, String> myMap = new HashMap<>();
myMap.put("A", "fortran");
myMap.put("B", "java");
myMap.put("C", "c++");
myMap.put("D", "php");
List<String> keysForFiltering = Arrays.asList("A", "C");
Predicate<Entry<String, String>> myPredicate = t -> keysForFiltering.contains(t.getKey());
Map<String, String> filteredMap = myMap
.entrySet().stream().filter(myPredicate)
.collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
System.out.println(filteredMap);
I have an Object which is of type Map<String, String> which has few entries. I expected to get a ClassCastException while casting this object to Map<String, Integer>. But the cast was successful. Why is it that this did not throw any exception?
Map<String, String> map1 = new HashMap<>();
map1.put("key1", "value1");
map1.put("key2", "value2");
Object o = map1;
Map<String, Integer> map2 = (Map<String, Integer>) o;
Edit: Casting from o not map1.
Generic-checking is made at compile time,while casting checking is done at the time of running the program.So You had got casting exception at run time.
You parse it as Integer.parseInt(String) and put the value into map2.
Are you sure that's right?
Your example fails to compile:
Error:(21, 60) java: incompatible types: java.util.Map<java.lang.String,java.lang.String> cannot be converted to java.util.Map<java.lang.String,java.lang.Integer>
However, changing from map1 to o, does compile:
//...
Object o = map1;
Map<String, Integer> map2 = (Map<String, Integer>) o;
Perhaps you're looking for something like this?
Map<String, String> map1 = new HashMap<>();
map1.put("key1", "1");
map1.put("key2", "2");
Map<String, Integer> map2 = new HashMap<>();
map1.forEach((key,value) -> map2.put(key, Integer.parseInt(value)));
So I have a method, setContainerSummaryMap, which takes in a Map<String, Map<String, Integer>>.
I also have a HashMap<Long, HashMap<String, Integer>>, called contIdDestQuanMapSoFar, which I am going to convert into a HashMap<String, HashMap<String, Integer>> with a HashMap<Long, String>, named contIdToScanIdMap, that maps the keys to one another. This method is below:
public HashMap<String, HashMap<String, Integer>> convertContSummaryMap() {
HashMap<String, HashMap<String, Integer>> toRet = new HashMap<String, HashMap<String, Integer>>();
for (Entry<Long, HashMap<String, Integer>> entry : contIdDestQuanMapSoFar.entrySet()) {
toRet.put(contIdToScanIdMap.get(entry.getKey()), entry.getValue());
}
return toRet;
}
The problem is, when I call the method setContainerSummaryMap(currentPlan.convertContSummaryMap()), I get an error, saying that it is not applicable for the arguments HashMap<String, HashMap<String, Integer>>. How would I modify the datatypes for this to work? Thanks.
You can redefine your setContainerSummaryMap method:
public void setContainerSummaryMap(Map<String, ? extends Map<String, Integer>> map)
or, as Louis already suggested change the return type of your convertContSummaryMap to match:
public HashMap<String, Map<String, Integer>> convertContSummaryMap()
To make this work, the type of toRet must be HashMap<String, Map<String, Integer>>. As #SLaks stated, a HashMap<String, Map> is not the same as a HashMap<String, HashMap>.
Of course, the Maps you'll actually put into it will still be HashMaps.