【问题标题】:How to convert List<String> to Map<String,List<String>> based on a delimeter如何基于分隔符将 List<String> 转换为 Map<String,List<String>>
【发布时间】:2019-05-31 06:12:32
【问题描述】:

我有一个字符串列表,例如:

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

我想在Map&lt;String, List&lt;String&gt;&gt; 中转换为:

AU = [5631]
CA = [1326]
US = [5423, 6321]

我已经尝试过这段代码,它可以工作,但在这种情况下,我必须创建一个新类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));

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;
    }
}

但我想知道,有什么方法可以在不引入新类的情况下将List&lt;String&gt; 转换为Map&lt;String, List&lt;String&gt;&gt;

【问题讨论】:

  • Java 缺少元组的问题再次来袭 :(

标签: java lambda java-8 java-stream


【解决方案1】:

你可以这样做:

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

一个更好的方法是,

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())));

更新

根据以下评论,这可以进一步简化为,

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

【讨论】:

  • 我看不出第二种方法更好。能详细点吗?
  • 我不确定这是否正确。如果您查看 String.split 的源代码,您会发现对 1 字符字符串进行了大量优化。
  • 后者的性能优于前者,因为它使用了预编译模式。
  • DELIMITER.splitAsStream(s).toArray(String[]::new) 为什么不直接使用DELIMITER.split(s)
  • 在尝试 1000 万个位置时发现奇怪的结果,预编译模式大约需要 0.830 秒,字符串拆分大约需要 0.58 秒,更好的方法并不是你提到的最佳方法。所以我使用s -&gt; s.split(":")
【解决方案2】:

试试这个

Map<String, List<String>> locationMap = locations.stream()
            .map(s ->  new AbstractMap.SimpleEntry<String,String>(s.split(":")[0], s.split(":")[1]))
            .collect(Collectors.groupingBy(Map.Entry::getKey,
                     Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

【讨论】:

  • 那里可以优化split的使用两次。
  • @Naman,对!刚刚复制了 OP 答案。...map(s-&gt;s.split(":")) .map(s -&gt; new AbstractMap.SimpleEntry&lt;String,String&gt;(s[0],s[1]))... 虽然@Ravindra Ranwala 更好
【解决方案3】:

您可以将代码按部分分组,将第一组作为键,第二组作为值,而不是先映射它

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

【讨论】:

  • 发帖后才注意到
【解决方案4】:

POJO 怎么样。与流相比,它看起来并不复杂。

public static Map<String, Set<String>> groupByCountry(List<String> locations) {
    Map<String, Set<String>> map = new HashMap<>();

    locations.forEach(location -> {
        String[] parts = location.split(":");
        map.compute(parts[0], (country, codes) -> {
            codes = codes == null ? new HashSet<>() : codes;
            codes.add(parts[1]);
            return codes;
        });
    });

    return map;
}

【讨论】:

    【解决方案5】:

    好像你的位置图需要按key排序,你可以试试下面的

    List<String> locations = Arrays.asList("US:5423", "US:6321", "CA:1326", "AU:5631");
    
        Map<String, List<String>> locationMap = locations.stream().map(str -> str.split(":"))
                .collect(() -> new TreeMap<String, List<String>>(), (map, parts) -> {
                    if (map.get(parts[0]) == null) {
                        List<String> list = new ArrayList<>();
                        list.add(parts[1]);
                        map.put(parts[0], list);
                    } else {
                        map.get(parts[0]).add(parts[1]);
                    }
                }, (map1, map2) -> {
                    map1.putAll(map2);
                });
    
        System.out.println(locationMap); // this outputs {AU=[5631], CA=[1326], US=[5423, 6321]}
    

    【讨论】:

    • Map 中排序无关紧要,键和值应该如前所述。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-01
    • 2021-02-28
    • 2021-12-21
    • 2020-04-29
    • 2016-07-21
    • 1970-01-01
    相关资源
    最近更新 更多