Issue while converting Set to Map - java

I am trying to convert Set to Map using java 8 using collectors.
Set<B2BUnitModel> payersList = .....
final Map<B2BUnitModel, List<B2BUnitPartnershipModel>> b2bUnitPartnershipMap = new HashMap<>();
final Map<B2BUnitModel, Object> map = payersList.stream().collect(Collectors.toMap(Function.identity(), Arrays::asList));
b2bUnitPartnershipMap.putAll(map); // Will throw Type Cast Error
I am not able to understand how do I convert Map values to B2BUnitPartnershipModel type since Arrays::asList only will return Object type.
Is there any way I can write something in Collectors.toMap(Function.identity(), Arrays::asList) itself so that the api will return the desired Map (Map<B2BUnitModel, List<B2BUnitPartnershipModel>> instead of Map<B2BUnitModel, Object>).
I want to create a Map with Set value as a key and empty B2BUnitPartnershipModel list.

Asuming B2BUnitModel is Key and B2BUnitPartnershipModel is Value the following code produces a map with keys from the set and empty lists as values using lambda expressions instead of method references.
Code
#Test
public void testMapCollector() {
Set<Key> keySet = new HashSet<>();
keySet.add(new Key("key1"));
keySet.add(new Key("key2"));
keySet.add(new Key("key3"));
Map<Key, List<Value>> map = keySet.stream().collect(
Collectors.toMap(k -> k, key -> new ArrayList<>()));
System.out.println(map);
}
class Key {
String key;
public Key(String value) {
this.key = value;
}
#Override
public String toString() {
return "Key [key=" + key + "]";
}
}
class Value {
}
Output
{Key [key=key1]=[], Key [key=key3]=[], Key [key=key2]=[]}

Your Arrays::asList causes problem. Try this:
Map<B2BUnitModel, List<B2BUnitPartnershipModel>> map = set.stream().collect(Collectors.toMap(Function.identity(), unit -> new ArrayList<>()));

Related

How to convert enum with attribute to map using java8 stream

this answer doesn't math: How to store enum to map using Java 8 stream API
I have an enum:
public enum SheetRows{
totalActive("Total active");
String value;
SheetRows(String value){
this.value = value;
}
public String getValueForTable() {
return value;
}
}
How to convert this enum to HashMap<SheetRows, String>?
I try to use:
HashMap<SheetRows, String> cellsMap = Arrays.asList(SheetRows.values()).stream()
.collect(Collectors.toMap(k -> k, v -> v.getValueForTable()));
but this code isn't compile.
The following should work, the problem is that you're trying to assign a Map to HashMap
Map<SheetRows, String> map = EnumSet.allOf(SheetRows.class)
.stream()
.collect(Collectors.toMap(Function.identity(), SheetRows::getValueForTable));
System.out.println("map = " + map); // map = {totalActive=Total active}
If you really need to return a HashMap, you can also use the overload that takes a supplier and a mergeFunction like hereunder.
The mergeFunction will never be called since your enums are unique, so just choose a random one.
HashMap<SheetRows, String> map = EnumSet.allOf(SheetRows.class)
.stream()
.collect(Collectors.toMap(Function.identity(), SheetRows::getValueForTable, (o1, o2) -> o1, HashMap::new));
System.out.println("map = " + map); // map = {totalActive=Total active}

finding a specific value in a hashmap

Is there a code for finding a specific value in a hashmap?
I want to use a for loop to convert values in a hashmap into an int.
for (int i = 0; i < items; i++) {
cost = Integer.parseInt(myHashmap);
}
can I even use .parseInt on a hashmap or is there another way to convert a place in a hashmap into a int?
Like String[3] is there a code to find a specific place in a hashmap?
To iterate over all values of a map, use the values method:
Map<Long, String> map = ...;
for (final String value = map.values()) {
System.out.println(value);
}
To find a specific value, iterate all values, check against your predicate and return if found:
String findValue(final Map<Long, String> map, final Predicate<String> condition) {
for (final String value = map.values()) {
if (condition.test(value)) {
return value;
}
}
return null;
}
To find the key for a given value, iterate the entry set of the map:
Long findKey(final Map<Long, String> map, final String value) {
for (final Map.Entry<Long, String> entry = map.entrySet()) {
if (Objects.equals(entry.getValue(), value)) {
return entry.getKey();
}
}
return null;
}
Of course, once you have a value (or a key), you can use it any way you like. That includes passing it as argument to Integer.parseInt.
myHashmap.values() will return all the values of the Map. Integer.parseInt(value) parses the String argument as a signed decimal integer object.
public static void main(String[] args) {
Map<String, String> myHashmap = new HashMap<>();
myHashmap.put("A", "10");
myHashmap.put("B", "20");
myHashmap.put("C", "30");
myHashmap.values().forEach(value -> {
System.out.println(Integer.parseInt(value));
// Rest of the logic
});
}

Java 8: How to get a value from a list contained as a map value?

I have the following situation:
I have a LinkedHashMap<> where the key type is a String and the values types varies: double, String, LinkedHashMap, etc.
I am trying to extract a value from a key of one of the LinkedHashMaps values which are a value of the main map.
For example, I'd like to get the result 1 from the following code (obviously it is a mess since it doesn't even compile):
Map<String, Object> input = new HashMap<>();
input.put("a", "1234");
input.put("b", "2345");
input.put("c", "3456");
input.put("d", new HashMap<String, String>());
HashMap<String, Object> input2 = (HashMap<String, Object>)(input.get("d"));
input2.put("d1", 1);
input2.put("d2", 2);
Optional<Integer> result = input.entrySet().stream()
.filter(e -> e.getKey().equals("d"))
.map(Map.Entry::getValue)
.filter(e -> e.getKey().equals("d1"))
.findFirst();
Where do I go wrong, and of course, what is the best way to get the result?
Thanks.
Once you use a Map with different value (and even key) types (and worse, nested maps). Then I suggest taking a step back and try to analyse what you've done. It seems that you're way better with a class than a Map. An example with your keys:
class YourClass {
String a;
String b;
String c;
YourOtherClass d;
}
class YourOtherClass {
Integer d1;
Integer d2;
}
I've omitted getters, setters and constructors for simplicity.
You can then create instances of those objects, like this:
YourOtherClass yoc = new YourOtherClass(1, 2);
YourClass yc = new YourClass("1234", "2345", "3456", yoc);
And then call the specific getter to receive a value with typesafety:
String a = yc.getA(); // works
Integer i = yc.getA(); // doesn't work
Or setting a new value via the setter:
yoc.setD1(4); // works
yoc.setD1("4"); // doesn't work
You're overcomplicating things imo. You could do it in a very straightforward manner. One liners are not always the ideal solutions.
I don't have the possibility to compile it, but it should be ok.
public Optional<Integer> getInnerValue(Map<String, Object> map, String outerKey, String innerKey) {
Object o = map.get(outerKey);
if (!(o instanceof Map)) {
return Optional.empty();
}
return Optional.ofNullable(((Map)o).get(innerKey));
}
Using a one-liner
public Optional<Integer> getInnerValue(Map<String, Object> map, String outerKey, String innerKey) {
return Optional.ofNullable(map.get(outerKey))
.filter(Map.class::isInstance)
.map(Map.class::cast)
.map(m -> m.get(innerKey))
.findFirst();
}

How to convert a Map to Arraylist of Objects?

Suppose I have having Json response like this:
{
"status": true,
"data": {
"29": "Hardik sheth",
"30": "Kavit Gosvami"
}
}
I am using Retrofit to parse Json response. As per this answer I will have to use Map<String, String> which will give all the data in Map. Now what I want is ArrayList<PojoObject>.
PojoObject.class
public class PojoObject {
private String mapKey, mapValue;
public String getMapKey() {
return mapKey;
}
public void setMapKey(String mapKey) {
this.mapKey = mapKey;
}
public String getMapValue() {
return mapValue;
}
public void setMapValue(String mapValue) {
this.mapValue = mapValue;
}
}
What is the best way to convert a Map<key,value> to a List<PojoObject>?
If you can expand your class to have a constructor taking the values as well:
map.entrySet()
.stream()
.map(e -> new PojoObject(e.getKey(), e.getValue()))
.collect(Collectors.toList());
If you can't:
map.entrySet()
.stream()
.map(e -> {
PojoObject po = new PojoObject();
po.setMapKey(e.getKey());
po.setMapValue(e.getValue());
return po;
}).collect(Collectors.toList());
Note that this uses Java 8 Stream API.
Looks like Java has exact POJO Map.Entry like you want. Hence, you can extract the entry set from map and iterate over the entry set like below or you can further convert the set to list like in next snippet and continue with your processing.
//fetch entry set from map
Set<Entry<String, String>> set = map.entrySet();
for(Entry<String, String> entry: set) {
System.out.println(entry.getKey() +"," + entry.getValue());
}
//convert set to list
List<Entry<String, String>> list = new ArrayList(set);
for(Entry<String, String> entry: list) {
System.out.println(entry.getKey() +"," + entry.getValue());
}
Try this
List<Value> list = new ArrayList<Value>(map.values());
Or
hashMap.keySet().toArray(); // returns an array of keys
hashMap.values().toArray(); // returns an array of values
Should be noted that the ordering of both arrays may not be the same.
or
hashMap.entrySet().toArray();
You can use this method to convert map to list
List<PojoObject> list = new ArrayList<PojoObject>(map.values());
Assuming:
Map <Key,Value> map;
ArrayList<Map<String,String>> list = new ArrayList<Map<String,String>>();
this may be the best way.

How to convert List to Map?

Recently I have conversation with a colleague about what would be the optimal way to convert List to Map in Java and if there any specific benefits of doing so.
I want to know optimal conversion approach and would really appreciate if any one can guide me.
Is this good approach:
List<Object[]> results;
Map<Integer, String> resultsMap = new HashMap<Integer, String>();
for (Object[] o : results) {
resultsMap.put((Integer) o[0], (String) o[1]);
}
With java-8, you'll be able to do this in one line using streams, and the Collectors class.
Map<String, Item> map =
list.stream().collect(Collectors.toMap(Item::getKey, item -> item));
Short demo:
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Test{
public static void main (String [] args){
List<Item> list = IntStream.rangeClosed(1, 4)
.mapToObj(Item::new)
.collect(Collectors.toList()); //[Item [i=1], Item [i=2], Item [i=3], Item [i=4]]
Map<String, Item> map =
list.stream().collect(Collectors.toMap(Item::getKey, item -> item));
map.forEach((k, v) -> System.out.println(k + " => " + v));
}
}
class Item {
private final int i;
public Item(int i){
this.i = i;
}
public String getKey(){
return "Key-"+i;
}
#Override
public String toString() {
return "Item [i=" + i + "]";
}
}
Output:
Key-1 => Item [i=1]
Key-2 => Item [i=2]
Key-3 => Item [i=3]
Key-4 => Item [i=4]
As noted in comments, you can use Function.identity() instead of item -> item, although I find i -> i rather explicit.
And to be complete note that you can use a binary operator if your function is not bijective. For example let's consider this List and the mapping function that for an int value, compute the result of it modulo 3:
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5, 6);
Map<String, Integer> map =
intList.stream().collect(toMap(i -> String.valueOf(i % 3), i -> i));
When running this code, you'll get an error saying java.lang.IllegalStateException: Duplicate key 1. This is because 1 % 3 is the same as 4 % 3 and hence have the same key value given the key mapping function. In this case you can provide a merge operator.
Here's one that sum the values; (i1, i2) -> i1 + i2; that can be replaced with the method reference Integer::sum.
Map<String, Integer> map =
intList.stream().collect(toMap(i -> String.valueOf(i % 3),
i -> i,
Integer::sum));
which now outputs:
0 => 9 (i.e 3 + 6)
1 => 5 (i.e 1 + 4)
2 => 7 (i.e 2 + 5)
List<Item> list;
Map<Key,Item> map = new HashMap<Key,Item>();
for (Item i : list) map.put(i.getKey(),i);
Assuming of course that each Item has a getKey() method that returns a key of the proper type.
Just in case this question isn't closed as a duplicate, the right answer is to use Google Collections:
Map<String,Role> mappedRoles = Maps.uniqueIndex(yourList, new Function<Role,String>() {
public String apply(Role from) {
return from.getName(); // or something else
}});
Short and sweet.
Using Java 8 you can do following :
Map<Key, Value> result= results
.stream()
.collect(Collectors.toMap(Value::getName,Function.identity()));
Value can be any object you use.
Alexis has already posted an answer in Java 8 using method toMap(keyMapper, valueMapper). As per doc for this method implementation:
There are no guarantees on the type, mutability, serializability, or
thread-safety of the Map returned.
So in case we are interested in a specific implementation of Map interface e.g. HashMap then we can use the overloaded form as:
Map<String, Item> map2 =
itemList.stream().collect(Collectors.toMap(Item::getKey, //key for map
Function.identity(), // value for map
(o,n) -> o, // merge function in case of conflict with keys
HashMap::new)); // map factory - we want HashMap and not any Map implementation
Though using either Function.identity() or i->i is fine but it seems Function.identity() instead of i -> i might save some memory as per this related answer.
Since Java 8, the answer by #ZouZou using the Collectors.toMap collector is certainly the idiomatic way to solve this problem.
And as this is such a common task, we can make it into a static utility.
That way the solution truly becomes a one-liner.
/**
* Returns a map where each entry is an item of {#code list} mapped by the
* key produced by applying {#code mapper} to the item.
*
* #param list the list to map
* #param mapper the function to produce the key from a list item
* #return the resulting map
* #throws IllegalStateException on duplicate key
*/
public static <K, T> Map<K, T> toMapBy(List<T> list,
Function<? super T, ? extends K> mapper) {
return list.stream().collect(Collectors.toMap(mapper, Function.identity()));
}
And here's how you would use it on a List<Student>:
Map<Long, Student> studentsById = toMapBy(students, Student::getId);
A List and Map are conceptually different. A List is an ordered collection of items. The items can contain duplicates, and an item might not have any concept of a unique identifier (key). A Map has values mapped to keys. Each key can only point to one value.
Therefore, depending on your List's items, it may or may not be possible to convert it to a Map. Does your List's items have no duplicates? Does each item have a unique key? If so then it's possible to put them in a Map.
There is also a simple way of doing this using Maps.uniqueIndex(...) from Google guava libraries
Universal method
public static <K, V> Map<K, V> listAsMap(Collection<V> sourceList, ListToMapConverter<K, V> converter) {
Map<K, V> newMap = new HashMap<K, V>();
for (V item : sourceList) {
newMap.put( converter.getKey(item), item );
}
return newMap;
}
public static interface ListToMapConverter<K, V> {
public K getKey(V item);
}
Using java-8 streams
Map<Integer, String> map = results.stream().collect(Collectors.toMap(e -> ((Integer) e[0]), e -> (String) e[1]));
Without java-8, you'll be able to do this in one line Commons collections, and the Closure class
List<Item> list;
#SuppressWarnings("unchecked")
Map<Key, Item> map = new HashMap<Key, Item>>(){{
CollectionUtils.forAllDo(list, new Closure() {
#Override
public void execute(Object input) {
Item item = (Item) input;
put(i.getKey(), item);
}
});
}};
like already said, in java-8 we have the concise solution by Collectors:
list.stream().collect(
groupingBy(Item::getKey)
)
and also, you can nest multiple group passing an other groupingBy method as second parameter:
list.stream().collect(
groupingBy(Item::getKey, groupingBy(Item::getOtherKey))
)
In this way, we'll have multi level map, like this: Map<key, Map<key, List<Item>>>
Many solutions come to mind, depending on what you want to achive:
Every List item is key and value
for( Object o : list ) {
map.put(o,o);
}
List elements have something to look them up, maybe a name:
for( MyObject o : list ) {
map.put(o.name,o);
}
List elements have something to look them up, and there is no guarantee that they are unique: Use Googles MultiMaps
for( MyObject o : list ) {
multimap.put(o.name,o);
}
Giving all the elements the position as a key:
for( int i=0; i<list.size; i++ ) {
map.put(i,list.get(i));
}
...
It really depends on what you want to achive.
As you can see from the examples, a Map is a mapping from a key to a value, while a list is just a series of elements having a position each. So they are simply not automatically convertible.
Here's a little method I wrote for exactly this purpose. It uses Validate from Apache Commons.
Feel free to use it.
/**
* Converts a <code>List</code> to a map. One of the methods of the list is called to retrive
* the value of the key to be used and the object itself from the list entry is used as the
* objct. An empty <code>Map</code> is returned upon null input.
* Reflection is used to retrieve the key from the object instance and method name passed in.
*
* #param <K> The type of the key to be used in the map
* #param <V> The type of value to be used in the map and the type of the elements in the
* collection
* #param coll The collection to be converted.
* #param keyType The class of key
* #param valueType The class of the value
* #param keyMethodName The method name to call on each instance in the collection to retrieve
* the key
* #return A map of key to value instances
* #throws IllegalArgumentException if any of the other paremeters are invalid.
*/
public static <K, V> Map<K, V> asMap(final java.util.Collection<V> coll,
final Class<K> keyType,
final Class<V> valueType,
final String keyMethodName) {
final HashMap<K, V> map = new HashMap<K, V>();
Method method = null;
if (isEmpty(coll)) return map;
notNull(keyType, Messages.getString(KEY_TYPE_NOT_NULL));
notNull(valueType, Messages.getString(VALUE_TYPE_NOT_NULL));
notEmpty(keyMethodName, Messages.getString(KEY_METHOD_NAME_NOT_NULL));
try {
// return the Method to invoke to get the key for the map
method = valueType.getMethod(keyMethodName);
}
catch (final NoSuchMethodException e) {
final String message =
String.format(
Messages.getString(METHOD_NOT_FOUND),
keyMethodName,
valueType);
e.fillInStackTrace();
logger.error(message, e);
throw new IllegalArgumentException(message, e);
}
try {
for (final V value : coll) {
Object object;
object = method.invoke(value);
#SuppressWarnings("unchecked")
final K key = (K) object;
map.put(key, value);
}
}
catch (final Exception e) {
final String message =
String.format(
Messages.getString(METHOD_CALL_FAILED),
method,
valueType);
e.fillInStackTrace();
logger.error(message, e);
throw new IllegalArgumentException(message, e);
}
return map;
}
A Java 8 example to convert a List<?> of objects into a Map<k, v>:
List<Hosting> list = new ArrayList<>();
list.add(new Hosting(1, "liquidweb.com", new Date()));
list.add(new Hosting(2, "linode.com", new Date()));
list.add(new Hosting(3, "digitalocean.com", new Date()));
//example 1
Map<Integer, String> result1 = list.stream().collect(
Collectors.toMap(Hosting::getId, Hosting::getName));
System.out.println("Result 1 : " + result1);
//example 2
Map<Integer, String> result2 = list.stream().collect(
Collectors.toMap(x -> x.getId(), x -> x.getName()));
Code copied from:
https://www.mkyong.com/java8/java-8-convert-list-to-map/
You can leverage the streams API of Java 8.
public class ListToMap {
public static void main(String[] args) {
List<User> items = Arrays.asList(new User("One"), new User("Two"), new User("Three"));
Map<String, User> map = createHashMap(items);
for(String key : map.keySet()) {
System.out.println(key +" : "+map.get(key));
}
}
public static Map<String, User> createHashMap(List<User> items) {
Map<String, User> map = items.stream().collect(Collectors.toMap(User::getId, Function.identity()));
return map;
}
}
For more details visit: http://codecramp.com/java-8-streams-api-convert-list-map/
I like Kango_V's answer, but I think it's too complex. I think this is simpler - maybe too simple. If inclined, you could replace String with a Generic marker, and make it work for any Key type.
public static <E> Map<String, E> convertListToMap(Collection<E> sourceList, ListToMapConverterInterface<E> converterInterface) {
Map<String, E> newMap = new HashMap<String, E>();
for( E item : sourceList ) {
newMap.put( converterInterface.getKeyForItem( item ), item );
}
return newMap;
}
public interface ListToMapConverterInterface<E> {
public String getKeyForItem(E item);
}
Used like this:
Map<String, PricingPlanAttribute> pricingPlanAttributeMap = convertListToMap( pricingPlanAttributeList,
new ListToMapConverterInterface<PricingPlanAttribute>() {
#Override
public String getKeyForItem(PricingPlanAttribute item) {
return item.getFullName();
}
} );
Apache Commons MapUtils.populateMap
If you don't use Java 8 and you don't want to use a explicit loop for some reason, try MapUtils.populateMap from Apache Commons.
MapUtils.populateMap
Say you have a list of Pairs.
List<ImmutablePair<String, String>> pairs = ImmutableList.of(
new ImmutablePair<>("A", "aaa"),
new ImmutablePair<>("B", "bbb")
);
And you now want a Map of the Pair's key to the Pair object.
Map<String, Pair<String, String>> map = new HashMap<>();
MapUtils.populateMap(map, pairs, new Transformer<Pair<String, String>, String>() {
#Override
public String transform(Pair<String, String> input) {
return input.getKey();
}
});
System.out.println(map);
gives output:
{A=(A,aaa), B=(B,bbb)}
That being said, a for loop is maybe easier to understand. (This below gives the same output):
Map<String, Pair<String, String>> map = new HashMap<>();
for (Pair<String, String> pair : pairs) {
map.put(pair.getKey(), pair);
}
System.out.println(map);
If you use Kotlin, there is an example:
listOf("one", "two").mapIndexed { i, it -> i to it }.toMap()
public class EmployeeDetailsFetchListToMap {
public static void main(String[] args) {
List<EmployeeDetailsFetch> list = new ArrayList<>();
list.add(new EmployeeDetailsFetch(1L, "vinay", 25000F));
list.add(new EmployeeDetailsFetch(2L, "kohli", 5000000F));
list.add(new EmployeeDetailsFetch(3L, "dhoni", 20000000F));
//adding id as key and map of id and student name
Map<Long, Map<Long, String>> map1 = list.stream()
.collect(
Collectors.groupingBy(
EmployeeDetailsFetch::getEmpId,
Collectors.toMap(
EmployeeDetailsFetch::getEmpId,
EmployeeDetailsFetch::getEmployeeName
)
)
);
System.out.println(map1);
//converting list into map of Student
//Adding id as Key and Value as Student into a map
Map<Long, EmployeeDetailsFetch> map = list.stream()
.collect(
Collectors.toMap(
EmployeeDetailsFetch::getEmpId,
EmployeeDetailsFetch -> EmployeeDetailsFetch
)
);
for(Map.Entry<Long, EmployeeDetailsFetch> m : map.entrySet()) {
System.out.println("key :" + m.getKey() + " Value : " + m.getValue());
}
}
}

Categories