collecting HashMap<String, List<String>> java 8
Asked Answered
Y

4

51

I want to be able to convert a List to a HashMap where the key is the elementName and the values is a list of something random (in this case its the Element Name). So in short I want (A->List(A), B->List(B), C-> List(C)). I tried using toMap() and passing it the keyMapper and ValueMapper but I get a compilation error. I would really appreciate if someone can help me out.

Thanks!

public static void main(String[] args) {
    // TODO Auto-generated method stub
    List<String> list = Arrays.asList("A","B","C","D");
    Map<String, List<String>> map = list.stream().map((element)->{
        Map<String, List<String>> map = new HashMap<>();
        map.put(element, Arrays.asList(element));
        return map;
    }).collect(??);
}


Function<Map<String, String>, String> key = (map) -> {
    return map.keySet().stream().findFirst().get();
};

Function<Map<String, String>, String> value = (map) -> {
    return map.values().stream().findFirst().get();
};

=== This worked for me

Thanks for all the help guys! @izstas "they should operate on the elements" helped a lot :). Actually this is what I was looking for to be exact

public static void test2 (){
    Function<Entry<String, List<String>>, String> key = (entry) -> {
        return entry.getKey();
    };
    Function<Entry<String, List<String>>, List<String>> value = (entry) -> {
        return new ArrayList<String>(entry.getValue());
    };
    BinaryOperator<List<String>> merge = (old, latest)->{
        old.addAll(latest);
        return old;
    };

    Map<String, List<String>> map1 = new HashMap<>();
    map1.put("A", Arrays.asList("A1", "A2"));
    map1.put("B", Arrays.asList("B1"));
    map1.put("D", Arrays.asList("D1"));

    Map<String, List<String>> map2 = new HashMap<>();
    map2.put("C", Arrays.asList("C1","C2"));
    map2.put("D", Arrays.asList("D2"));

    Stream<Map<String, List<String>>> stream =Stream.of(map1, map2);
    System.out.println(stream.flatMap((map)->{
        return map.entrySet().stream(); 
    }).collect(Collectors.toMap(key, value, merge)));
}
Yaakov answered 23/7, 2014 at 17:27 Comment(3)
What is the compiler error?Angieangil
Post your source code, see How to create a Minimal, Complete, and Verifiable exampleChadwell
I am not sure but as far as I know in your map part whatever you have from left side has to match with the right side, so you should return element not map I think :)Babbitt
C
26

Functions key and value you have defined in your code are not correct because they should operate on the elements of your list, and your elements are not Maps.

The following code works for me:

List<String> list = Arrays.asList("A", "B", "C", "D");
Map<String, List<String>> map = list.stream()
        .collect(Collectors.toMap(Function.identity(), Arrays::asList));

First argument to Collectors.toMap defines how to make a key from the list element (leaving it as is), second argument defines how to make a value (making an ArrayList with a single element).

Compensatory answered 23/7, 2014 at 17:57 Comment(5)
Did you consider replacing (element) -> element with Function.<String>identity()?Ornithopod
@Ornithopod Oh, that's a nice suggestion. I will update my answer. By the way, it appears to work for me without having to specify the type parameter explicitly.Compensatory
Cool. Yeah the type is not needed. It was a copy/paste.Ornithopod
I've updated the question with the solution I was looking for. Thanks !Yaakov
This code do not works for multiple elements, try with List<String> list = Arrays.asList("A", "B", "C", "D", "A");Ultramicrometer
U
102

You can use the groupingBy method to manage aggregation, for example:

public static void main(String[] args) {
    List<String> list = Arrays.asList("A", "B", "C", "D", "A");
    Map<String, List<String>> map = list.stream().collect(Collectors.groupingBy(Function.identity()));
}

If you want more flexibility (for example to map the value and return a Set instead of a List) you can always use the groupingBy method with more parameters as specified in javadoc:

Map<City, Set<String>> namesByCity = people.stream().collect(Collectors.groupingBy(Person::getCity, mapping(Person::getLastName, toSet())));
Ultramicrometer answered 16/7, 2015 at 14:9 Comment(3)
groupingBy does exist??Iva
I fixed the example sorry. The method groupingBy is a static method in java.util.stream.Collectors.Ultramicrometer
Brilliant on the Map<City, Set<String>>. Thanks!Elicia
C
26

Functions key and value you have defined in your code are not correct because they should operate on the elements of your list, and your elements are not Maps.

The following code works for me:

List<String> list = Arrays.asList("A", "B", "C", "D");
Map<String, List<String>> map = list.stream()
        .collect(Collectors.toMap(Function.identity(), Arrays::asList));

First argument to Collectors.toMap defines how to make a key from the list element (leaving it as is), second argument defines how to make a value (making an ArrayList with a single element).

Compensatory answered 23/7, 2014 at 17:57 Comment(5)
Did you consider replacing (element) -> element with Function.<String>identity()?Ornithopod
@Ornithopod Oh, that's a nice suggestion. I will update my answer. By the way, it appears to work for me without having to specify the type parameter explicitly.Compensatory
Cool. Yeah the type is not needed. It was a copy/paste.Ornithopod
I've updated the question with the solution I was looking for. Thanks !Yaakov
This code do not works for multiple elements, try with List<String> list = Arrays.asList("A", "B", "C", "D", "A");Ultramicrometer
Y
9

Thanks for all the help guys! @izstas "they should operate on the elements" helped a lot :). Actually this is what I was looking for to be exact

public static void test2 (){
    Function<Entry<String, List<String>>, String> key = (entry) -> {
        return entry.getKey();
    };
    Function<Entry<String, List<String>>, List<String>> value = (entry) -> {
        return new ArrayList<String>(entry.getValue());
    };
    BinaryOperator<List<String>> merge = (old, latest)->{
        old.addAll(latest);
        return old;
    };

    Map<String, List<String>> map1 = new HashMap<>();
    map1.put("A", Arrays.asList("A1", "A2"));
    map1.put("B", Arrays.asList("B1"));
    map1.put("D", Arrays.asList("D1"));

    Map<String, List<String>> map2 = new HashMap<>();
    map2.put("C", Arrays.asList("C1","C2"));
    map2.put("D", Arrays.asList("D2"));

    Stream<Map<String, List<String>>> stream =Stream.of(map1, map2);
    System.out.println(stream.flatMap((map)->{
        return map.entrySet().stream(); 
    }).collect(Collectors.toMap(key, value, merge)));
}
Yaakov answered 24/7, 2014 at 13:53 Comment(0)
S
0

I had a similar situation where I have a list of Map.Entry<X, Y> and was trying to aggregate them into a Map<X, List>. Here just the list.stream().collect(Collectors.groupingBy(Entry::getKey)) does not work simply.

The way to go was:

list.stream()
.collect(Collectors.groupingBy(Entry::getKey, Collectors.mapping(Entry::getValue, Collectors.toList()))))

Kudos to Piet from here: https://coderanch.com/t/755986/java/Create-Map-existing-List-Entry

Salad answered 22/1 at 8:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.