Assign unique IDs to objects using Java 8 streams - java

static <T> Map<T, Integer> assignIds(Collection<T> objects);
I want to write a function that takes a collection of unique objects and assigns a different ID number to each. The ID numbers should be assigned sequentially.
I could easily do this with an explicit loop like:
Map<T, Integer> ids = new HashMap<>();
int id = 0;
for (T object: objects) {
ids.put(object, id++);
}
Is there an elegant way to do this with the new Java 8 Stream API?

Here's one way:
static <T> Map<T, Integer> assignIds(Collection<T> objects) {
AtomicInteger ai = new AtomicInteger();
return objects.stream()
.collect(Collectors.toMap(o -> o, o -> ai.getAndIncrement()));
}
The above solution could also make use of parallelStream() instead of stream().
Here's another that works sequentially:
static <T> Map<T, Integer> assignIds(Collection<T> objects) {
Map<T, Integer> result = new HashMap<>();
objects.stream().forEachOrdered(o -> result.put(o, result.size()));
return result;
}
Building upon ZouZou's answer...
static <T> Map<T, Integer> assignIds(Collection<T> objects) {
OfInt ids = IntStream.range(0, objects.size()).iterator();
return objects.stream().collect(Collectors.toMap(o -> o, o -> ids.next()));
}
The idiomatic way to do this in for instance Scala would be to use zipWithIndex. There's no such method in the Java 8 Streams API, not even a zip method which you could combine with an IntStream.

You could use a primitive iterator to generate the ids:
static <T> Map<T, Integer> assignIds(Collection<T> objects) {
PrimitiveIterator.OfInt iterator = IntStream.iterate(0, x -> x + 1)
.limit(objects.size())
.iterator();
return objects.stream().collect(Collectors.toMap(obj -> obj, id -> iterator.next()));
}
You might be interested to use the protonpack library which defines some utility methods for Streams (such as zipWithIndex). So it could looks like this:
static <T> Map<T, Long> assignIds(Collection<T> objects) {
return StreamUtils.zipWithIndex(objects.stream())
.collect(Collectors.toMap(Indexed::getValue, Indexed::getIndex));
}

Related

How to covert List to Map<T, <K, List<Person>>> use java lambda?

How to convert list to Map<Integer, Map<Integer, List<Person>>> use java lambda?
I only know this:
private static Map<Dish.Type, List<String>> groupDishNamesByType() {
return menu.stream().collect(groupingBy(Dish::getType, mapping(Dish::getName, toList())));
}
I only know this:
public static <T, F> Map<F, T> listToMap(List<T> list, Function<T, F> f) {
return list.stream().collect(Collectors.toMap(f, obj -> obj));
}
But I don't know how to write use Map to be a key in Map. Pherhaps:
class Person{
int age;
int cityCode;
String name;
}
method:
// Map<age, Map<cityCode, List<Person>>>
public Map<Integer, Map<Integer, List<Person>>> covertListToMap(List<Person> list){
// TODO: How to make List<Person> to Map<age, Map<cityCode, List<Person>>>
}
Thank you floor-1 #Master chief.
But now I found the other problem:
When the build first group's key , get a key1result, I want to use it in second group, How to do it? -_-
Function<Person, Integer> key1 = (Person p) -> {
// do many sth then get a key1Result:
int key1Result = p.getAge() * new Random(10).nextInt();
return key1Result;
};
Function<Person, Integer> key2 = (Person p) -> {
// Question: how to get and use Function key1 key1Result:
int key1Result = 0;
int result = p.getCityCode() + key1Result;
return result;
};
Map<Integer, Map<Integer, List<Person>>> collect = list.stream().collect(groupingBy(key1, groupingBy(key2)));
Map<Integer, Map<Integer, List<Person>>> collect = personList.stream().collect(Collectors
.groupingBy(Person::getAge, Collectors.groupingBy(Person::getCityCode)));

How to insert the same value for multiple keys of an HashMap using Java Streams

Say I have a HashMap and I want to insert the same value to a list of keys. How can I do this with Java 8 without iterating through all the keys and inserting the value? This is more of a Java Streams question.
Here is the straight forward way of doing it. This is a sample code that I wrote to demonstrate what I wanted to achieve.
public void foo(List<String> keys, Integer value) {
Map<String, Integer> myMap = new HashMap<>();
for (String key : keys) {
myMap.put(key, value);
}
}
Is there a simpler way of doing the above using Java 8 streams? How can I avoid the for loop using Java 8 streams. Thanks!
[Edit-1] A better code snippet below.
public void foo() {
Map<String, Integer> myMap = new HashMap<>();
List<String> keys = getKeysFromAnotherFunction();
Integer value = getValueToBeInserted(); // Difficult to show my actual use case. Imagine that some value is getting computed which has to be inserted for the keys.
for (String key : keys) {
myMap.put(key, value);
}
List<String> keys2 = getNextSetOfKeys();
Integer newValue = getValueToBeInserted();
for (String key : keys2) {
myMap.put(key, newValue);
}
}
Using collector, something like:
Map<String, Integer> myMap = keys.stream()
.collect(Collectors.toMap(key -> key,
val -> value, (a, b) -> b));
I think that your question is about factoring out some piece of code more than converting traditional for loops into stream constructs.
Suppose you have the following generic utility method:
public static <K, V, M extends Map<K, V>> M fillMap(
Supplier<List<K>> keysFactory,
Supplier<V> singleValueFactory,
Supplier<M> mapFactory) {
M map = mapFactory.get();
List<K> keys = keysFactory.get();
V singleValue = singleValueFactory.get();
keys.forEach(k -> map.put(k, singleValue));
return map;
}
Then, you could use the above method as follows:
Map<String, Integer> myMap = fillMap(() -> getKeysFromAnotherFunction(),
() -> getValueToBeInserted(),
HashMap::new); // create HashMap
myMap = fillMap(() -> getNextSetOfKeys(),
() -> getValueToBeInserted(),
() -> myMap); // use previously created map
There are variants for the code above, i.e., the method could receive a Map<K, V> instance instead of a Supplier<Map<K, V>>, or it might even be overloaded to support both variants.

Multiple aggregate functions in Java 8 Stream API

I have a class defined like
public class TimePeriodCalc {
private double occupancy;
private double efficiency;
private String atDate;
}
I would like to perform the following SQL statement using Java 8 Stream API.
SELECT atDate, AVG(occupancy), AVG(efficiency)
FROM TimePeriodCalc
GROUP BY atDate
I tried :
Collection<TimePeriodCalc> collector = result.stream().collect(groupingBy(p -> p.getAtDate(), ....
What can be put into the code to select multiple attributes ? I'm thinking of using multiple Collectors but really don't know how to do so.
To do it without a custom Collector (not streaming again on the result), you could do it like this. It's a bit dirty, since it is first collecting to Map<String, List<TimePeriodCalc>> and then streaming that list and get the average double.
Since you need two averages, they are collected to a Holder or a Pair, in this case I'm using AbstractMap.SimpleEntry
Map<String, SimpleEntry<Double, Double>> map = Stream.of(new TimePeriodCalc(12d, 10d, "A"), new TimePeriodCalc(2d, 16d, "A"))
.collect(Collectors.groupingBy(TimePeriodCalc::getAtDate,
Collectors.collectingAndThen(Collectors.toList(), list -> {
double occupancy = list.stream().collect(
Collectors.averagingDouble(TimePeriodCalc::getOccupancy));
double efficiency = list.stream().collect(
Collectors.averagingDouble(TimePeriodCalc::getEfficiency));
return new AbstractMap.SimpleEntry<>(occupancy, efficiency);
})));
System.out.println(map);
Here's a way with a custom collector. It only needs one pass, but it's not very easy, especially because of generics...
If you have this method:
#SuppressWarnings("unchecked")
#SafeVarargs
static <T, A, C extends Collector<T, A, Double>> Collector<T, ?, List<Double>>
averagingManyDoubles(ToDoubleFunction<? super T>... extractors) {
List<C> collectors = Arrays.stream(extractors)
.map(extractor -> (C) Collectors.averagingDouble(extractor))
.collect(Collectors.toList());
class Acc {
List<A> averages = collectors.stream()
.map(c -> c.supplier().get())
.collect(Collectors.toList());
void add(T elem) {
IntStream.range(0, extractors.length).forEach(i ->
collectors.get(i).accumulator().accept(averages.get(i), elem));
}
Acc merge(Acc another) {
IntStream.range(0, extractors.length).forEach(i ->
averages.set(i, collectors.get(i).combiner()
.apply(averages.get(i), another.averages.get(i))));
return this;
}
List<Double> finish() {
return IntStream.range(0, extractors.length)
.mapToObj(i -> collectors.get(i).finisher().apply(averages.get(i)))
.collect(Collectors.toList());
}
}
return Collector.of(Acc::new, Acc::add, Acc::merge, Acc::finish);
}
This receives an array of functions that will extract double values from each element of the stream. These extractors are converted to Collectors.averagingDouble collectors and then the local Acc class is created with the mutable structures that are used to accumulate the averages for each collector. Then, the accumulator function forwards to each accumulator, and so with the combiner and finisher functions.
Usage is as follows:
Map<String, List<Double>> averages = list.stream()
.collect(Collectors.groupingBy(
TimePeriodCalc::getAtDate,
averagingManyDoubles(
TimePeriodCalc::getOccupancy,
TimePeriodCalc::getEfficiency)));
Assuming that your TimePeriodCalc class has all the necessary getters, this should get you the list you want:
List<TimePeriodCalc> result = new ArrayList<>(
list.stream()
.collect(Collectors.groupingBy(TimePeriodCalc::getAtDate,
Collectors.collectingAndThen(Collectors.toList(), TimePeriodCalc::avgTimePeriodCalc)))
.values()
);
Where TimePeriodCalc.avgTimePeriodCalc is this method in the TimePeriodCalc class:
public static TimePeriodCalc avgTimePeriodCalc(List<TimePeriodCalc> list){
return new TimePeriodCalc(
list.stream().collect(Collectors.averagingDouble(TimePeriodCalc::getOccupancy)),
list.stream().collect(Collectors.averagingDouble(TimePeriodCalc::getEfficiency)),
list.get(0).getAtDate()
);
}
The above can be combined into this monstrosity:
List<TimePeriodCalc> result = new ArrayList<>(
list.stream()
.collect(Collectors.groupingBy(TimePeriodCalc::getAtDate,
Collectors.collectingAndThen(
Collectors.toList(), a -> {
return new TimePeriodCalc(
a.stream().collect(Collectors.averagingDouble(TimePeriodCalc::getOccupancy)),
a.stream().collect(Collectors.averagingDouble(TimePeriodCalc::getEfficiency)),
a.get(0).getAtDate()
);
}
)))
.values());
With input:
List<TimePeriodCalc> list = new ArrayList<>();
list.add(new TimePeriodCalc(10,10,"a"));
list.add(new TimePeriodCalc(10,10,"b"));
list.add(new TimePeriodCalc(10,10,"c"));
list.add(new TimePeriodCalc(5,5,"a"));
list.add(new TimePeriodCalc(0,0,"b"));
This would give:
TimePeriodCalc [occupancy=7.5, efficiency=7.5, atDate=a]
TimePeriodCalc [occupancy=5.0, efficiency=5.0, atDate=b]
TimePeriodCalc [occupancy=10.0, efficiency=10.0, atDate=c]
You can chain multiple attributes like this:
Collection<TimePeriodCalc> collector = result.stream().collect(Collectors.groupingBy(p -> p.getAtDate(), Collectors.averagingInt(p -> p.getOccupancy())));
If you want more, you get the idea.

Idiomatically enumerating a Stream of objects in Java 8

How can one idiomatically enumerate a Stream<T> which maps each T instance to a unique integer using Java 8 stream methods (e.g. for an array T[] values, creating a Map<T,Integer> where Map.get(values[i]) == i evaluates to true)?
Currently, I'm defining an anonymous class which increments an int field for use with the Collectors.toMap(..) method:
private static <T> Map<T, Integer> createIdMap(final Stream<T> values) {
return values.collect(Collectors.toMap(Function.identity(), new Function<T, Integer>() {
private int nextId = 0;
#Override
public Integer apply(final T t) {
return nextId++;
}
}));
}
However, is there not a more concise/elegant way of doing this using the Java 8 stream API? — bonus points if it can be safely parallelized.
Your approach will fail, if there is a duplicate element.
Besides that, your task requires mutable state, hence, can be solved with Mutable reduction. When we populate a map, we can simple use the map’s size to get an unused id.
The trickier part is the merge operation. The following operation simply repeats the assignments for the right map, which will handle potential duplicates.
private static <T> Map<T, Integer> createIdMap(Stream<T> values) {
return values.collect(HashMap::new, (m,t) -> m.putIfAbsent(t,m.size()),
(m1,m2) -> {
if(m1.isEmpty()) m1.putAll(m2);
else m2.keySet().forEach(t -> m1.putIfAbsent(t, m1.size()));
});
}
If we rely on unique elements, or insert an explicit distinct(), we can use
private static <T> Map<T, Integer> createIdMap(Stream<T> values) {
return values.distinct().collect(HashMap::new, (m,t) -> m.put(t,m.size()),
(m1,m2) -> { int leftSize=m1.size();
if(leftSize==0) m1.putAll(m2);
else m2.forEach((t,id) -> m1.put(t, leftSize+id));
});
}
I would do it in this way:
private static <T> Map<T, Integer> createIdMap2(final Stream<T> values) {
List<T> list = values.collect(Collectors.toList());
return IntStream.range(0, list.size()).boxed()
.collect(Collectors.toMap(list::get, Function.identity()));
}
For sake or parallelism, it can be changed to
return IntStream.range(0, list.size()).parallel().boxed().
(...)
Comparing to convert the input stream to List first in the solution provided by Andremoniy. I would prefer to do it in different way because we don't know the cost of "toList()" and "list.get(i)", and it's unnecessary to create an extra List, which could be small or bigger
private static <T> Map<T, Integer> createIdMap2(final Stream<T> values) {
final MutableInt idx = MutableInt.of(0); // Or: final AtomicInteger idx = new AtomicInteger(0);
return values.collect(Collectors.toMap(Function.identity(), e -> idx.getAndIncrement()));
}
Regardless to the question, I think it's a bad design to pass streams as parameters in a method.

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