Java 8: How to convert List<String> to Map<String,List<String>>?

Vin*_*nki 17 java lambda java-8 java-stream

I have a List of String like:

List<String> locations = Arrays.asList("US:5423","US:6321","CA:1326","AU:5631");
Run Code Online (Sandbox Code Playgroud)

And I want to convert in Map<String, List<String>> as like:

AU = [5631]
CA = [1326]
US = [5423, 6321]
Run Code Online (Sandbox Code Playgroud)

I have tried this code and it works but in this case, I have to create a new class GeoLocation.java.

List<String> locations=Arrays.asList("US:5423", "US:6321", "CA:1326", "AU:5631");
Map<String, List<String>> locationMap = locations
        .stream()
        .map(s -> new GeoLocation(s.split(":")[0], s.split(":")[1]))
        .collect(
                Collectors.groupingBy(GeoLocation::getCountry,
                Collectors.mapping(GeoLocation::getLocation, Collectors.toList()))
        );

locationMap.forEach((key, value) -> System.out.println(key + " = " + value));
Run Code Online (Sandbox Code Playgroud)

GeoLocation.java

private class GeoLocation {
    private String country;
    private String location;

    public GeoLocation(String country, String location) {
        this.country = country;
        this.location = location;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }
}
Run Code Online (Sandbox Code Playgroud)

But I want to know, Is there any way to convert List<String> to Map<String, List<String>> without introducing new class.

Rav*_*ala 24

You may do it like so:

Map<String, List<String>> locationMap = locations.stream()
        .map(s -> s.split(":"))
        .collect(Collectors.groupingBy(a -> a[0],
                Collectors.mapping(a -> a[1], Collectors.toList())));
Run Code Online (Sandbox Code Playgroud)

A much more better approach would be,

private static final Pattern DELIMITER = Pattern.compile(":");

Map<String, List<String>> locationMap = locations.stream()
    .map(s -> DELIMITER.splitAsStream(s).toArray(String[]::new))
        .collect(Collectors.groupingBy(a -> a[0], 
            Collectors.mapping(a -> a[1], Collectors.toList())));
Run Code Online (Sandbox Code Playgroud)

Update

As per the following comment, this can be further simplified to,

Map<String, List<String>> locationMap = locations.stream().map(DELIMITER::split)
    .collect(Collectors.groupingBy(a -> a[0], 
        Collectors.mapping(a -> a[1], Collectors.toList())));
Run Code Online (Sandbox Code Playgroud)

  • 当在1000万个位置尝试时发现奇怪的结果,预编译的模式花费约0.830秒,字符串拆分花费约0.58秒,更好的方法并不是您提到的最佳方法。所以我用`s-&gt; s.split(“:”)` (7认同)
  • DELIMITER.splitAsStream(s).toArray(String [] :: new)为什么不只使用DELIMITER.split(s)? (4认同)
  • 我不认为第二种方法更好。你能详细说明一下吗? (3认同)