Java 8 List<V> into Map<K, V>
Based on Collectors
documentation it's as simple as:
Map<String, Choice> result =
choices.stream().collect(Collectors.toMap(Choice::getName,
Function.identity()));
Java 8 convert Map<K, List<V>> to Map<V, List<K>>
I think you were close, you would need to flatMap
those entries to a Stream
and collect from there. I've used the already present SimpleEntry
, but you can use a Pair
of some kind too.
initialMap.entrySet()
.stream()
.flatMap(entry -> entry.getValue().stream().map(v -> new SimpleEntry<>(entry.getKey(), v)))
.collect(Collectors.groupingBy(
Entry::getValue,
Collectors.mapping(Entry::getKey, Collectors.toList())
));
Well, if you don't want to create the extra overhead of those SimpleEntry
instances, you could do it a bit different:
Map<Integer, List<String>> result = new HashMap<>();
initialMap.forEach((key, values) -> {
values.forEach(value -> result.computeIfAbsent(value, x -> new ArrayList<>()).add(key));
});
How to convert List<Map<K,V>> into Map<K,List<V>> In java
The alternatives could look like :
static <K,V> Map<K,List<V>> getMapFromTheListOld_1(List<Map<K,V>> list){
Map<K,List<V>> map = new HashMap<>();
for(Map<K,V> m : list){
for(Map.Entry<K,V> e : m.entrySet()){
if( !map.containsKey(e.getKey())){
map.put(e.getKey(), new ArrayList<>());
}
map.get(e.getKey()).add(e.getValue());
}
}
return map;
}
You could simplify the inner loop using Map#computeIfAbsent
:
static <K,V> Map<K,List<V>> getMapFromTheListOld_2(List<Map<K,V>> list){
Map<K,List<V>> map = new HashMap<>();
for(Map<K,V> m : list){
for(Map.Entry<K,V> e : m.entrySet()){
map.computeIfAbsent(e.getKey(), k -> new ArrayList<>()).add(e.getValue());
}
}
return map;
}
But IMO both approachs are not easier than your one-liner using streams. You could add some new lines to make it more readable though:
static <K,V> Map<K,List<V>> getMapFromTheList(List<Map<K,V>> list){
return list.stream()
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.groupingBy(
Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
}
How to convert List<V> into Map<K, List<V>>, with Java 8 streams and custom List and Map suppliers?
You could have the following:
public Map<Integer, List<String>> getMap(List<String> strings) {
return strings.stream().collect(
Collectors.groupingBy(String::length, HashMap::new, Collectors.toCollection(ArrayList::new))
);
}
The collector groupingBy(classifier, mapFactory, downstream)
can be used to specify which type of map is wanted, by passing it a supplier of the wanted map for the mapFactory
. Then, the downstream collector, which is used to collect elements grouped to the same key, is toCollection(collectionFactory)
, which enables to collect into a collection obtained from the given supplier.
This makes sure that the map returned is a HashMap
and that the lists, in each value, are ArrayList
. Note that if you want to return specific implementations of map and collection, then you most likely want the method to return those specific types as well, so you can use their properties.
If you only want to specify a collection supplier, and keep groupingBy
default map, you can just omit the supplier in the code above and use the two arguments overload:
public Map<Integer, List<String>> getMap(List<String> strings) {
return strings.stream().collect(
Collectors.groupingBy(String::length, Collectors.toCollection(ArrayList::new))
);
}
As a side-note, you could have a generic method for that:
public <K, V, C extends Collection<V>, M extends Map<K, C>> M getMap(List<V> list,
Function<? super V, ? extends K> classifier, Supplier<M> mapSupplier, Supplier<C> collectionSupplier) {
return list.stream().collect(
Collectors.groupingBy(classifier, mapSupplier, Collectors.toCollection(collectionSupplier))
);
}
The advantage with this declaration is that you can now use it to have specific HashMap
of ArrayList
s as result, or LinkedHashMap
of LinkedLists
s, if the caller wishes it:
HashMap<Integer, ArrayList<String>> m = getMap(Arrays.asList("foo", "bar", "toto"),
String::length, HashMap::new, ArrayList::new);
LinkedHashMap<Integer, LinkedList<String>> m2 = getMap(Arrays.asList("foo", "bar", "toto"),
String::length, LinkedHashMap::new, LinkedList::new);
but, at that point, it may be simpler to directly use the groupingBy
in the code...
Convert Map<K, List<V>> to Map<K, V> where V have 2 lists of objects
What you've got looks pretty good already. Just replace the value mapper with e -> convert(e.getValue())
where convert
is like the function below that converts a List<ObjectB>
into an ObjectC
:
ObjectC convert(List<ObjectB> list) {
ObjectC c = new ObjectC();
for (ObjectB b : list) {
c.ba.add(b.ba);
c.bb.add(b.bb);
}
return c;
}
Or if you'd prefer to stick with just using streams, try Stream#collect
like so:
e.getValue().stream().collect(
() -> new ObjectC(),
(c, b) -> {
c.ba.add(b.ba);
c.bb.add(b.bb);
},
(c1, c2) -> {
c1.ba.addAll(c2.ba);
c1.bb.addAll(c2.bb);
}
)
Java 8 List<V> into Map<K, V> with Function
Collectors.toMap
takes two functions, and neither of your arguments fits.
You should use:
Map<Pair<Type, Boolean>, BiConsumer<Pair<Type, Boolean>, Parameters>> map =
set.stream()
.collect(Collectors.toMap(el -> Pair.of(el, Boolean.TRUE),
el -> this::methodAcceptingMap));
The expression Pair.of(t, Boolean.TRUE)
is simply not of a Function
type. And this::methodAcceptingMap
could fit the signature of a BiConsumer
, but the method requires a Function. So el -> this::methodAcceptingMap
should be used as a function that takes a stream element and returns your BiConsumer
.
Note that the assignment context (map =
) is important in this case. Without it, the target type of these lambda expressions will be missing and compilation will fail.
Convert list of Object to Map<Key, List<Object>> with java 8 stream
There's a collector to group things, consider the following example
Application.java
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Application {
private static class User {
private final String name;
private final Integer companyId;
public User(String name, Integer companyId) {
this.name = name;
this.companyId = companyId;
}
public String getName() {
return name;
}
public Integer getCompanyId() {
return companyId;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", companyId=" + companyId +
'}';
}
}
public static void main(String[] args) {
final List<User> users = Arrays.asList(new User("A", 1), new User("B", 1), new User("C", 2));
final Map<Integer, List<User>> byCompanyId = users.stream()
.collect(Collectors.groupingBy(User::getCompanyId));
System.out.println(byCompanyId);
}
}
that will print
{1=[User{name='A', companyId=1}, User{name='B', companyId=1}], 2=[User{name='C', companyId=2}]}
List<Object[]> to Map<K, V> in java 8
I think your current 'one-liner' is fine as is. But if you don't particularly like the magic indices built into the command then you could encapsulate in an enum:
enum Column {
CATEGORY(0),
COUNT(1);
private final int index;
Column(int index) {
this.index = index;
}
public int getIntValue(Object[] row) {
return (int)row[index]);
}
public String getStringValue(Object[] row) {
return (String)row[index];
}
}
Then you're extraction code gets a bit clearer:
list.stream().collect(Collectors.toMap(CATEGORY::getStringValue, COUNT::getIntValue));
Ideally you'd add a type field to the column and check the correct method is called.
While outside the scope of your question, ideally you would create a class representing the rows which encapsulates the query. Something like the following (skipped the getters for clarity):
class CategoryCount {
private static final String QUERY = "
select category, count(*)
from table
group by category";
private final String category;
private final int count;
public static Stream<CategoryCount> getAllCategoryCounts() {
list<Object[]> results = runQuery(QUERY);
return Arrays.stream(results).map(CategoryCount::new);
}
private CategoryCount(Object[] row) {
category = (String)row[0];
count = (int)row[1];
}
}
That puts the dependency between the query and the decoding of the rows into the same class and hides all the unnecessary details from the user.
Then creating your map becomes:
Map<String,Integer> categoryCountMap = CategoryCount.getAllCategoryCounts()
.collect(Collectors.toMap(CategoryCount::getCategory, CategoryCount::getCount));
Related Topics
Standard Concise Way to Copy a File in Java
What's the Difference Between Instant and Localdatetime
How to Handle Cors Using Jax-Rs With Jersey
How to Escape Text For Regular Expression in Java
Split String to Equal Length Substrings in Java
How to Open a New Tab Using Selenium Webdriver in Java
Get Type of a Generic Parameter in Java With Reflection
How to Do Url Decoding in Java
Can an Abstract Class Have a Constructor
@Requestparam VS @Pathvariable
How to Convert Number to Words in Java
What Do 3 Dots Next to a Parameter Type Mean in Java
What Does Servletcontext.Getrealpath("/") Mean and When Should I Use It
How to Implement a Single Instance Java Application
Why Is Volatile Used in Double Checked Locking
Why in Java 8 Split Sometimes Removes Empty Strings At Start of Result Array