Java 8 List≪V≫ into Map≪K, V≫

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 ArrayLists as result, or LinkedHashMap of LinkedListss, 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



Leave a reply



Submit