【问题标题】:Java 11: convert List<String> to TreeMap<String, List<String>> using CollectorsJava 11:使用收集器将 List<String> 转换为 TreeMap<String, List<String>>
【发布时间】:2021-03-29 09:57:28
【问题描述】:

我有一个这样的列表

List<String> customList = Arrays.asList(
   "5000  Buruli ulcer is an infectious disease",
   "6000  characterized by the development",
   "7000  of painless open wounds.",
   "8000  The disease largely occurs in",
   "10000  sub-Saharan Africa and Australia."
);

我想像这样将List 转换为TreeMap&lt;String, List&lt;String&gt;&gt;

"5000", ["Buruli", "ulcer", "is", "an", "infectious", "disease"]
"6000", ["characterized", "by", "the", "development"]
// etc

到目前为止我的代码:

TreeMap<String, List<String[]>> collect = customList.stream()
      .map(s -> s.split("  ", 2))
      .collect(Collectors
         .groupingBy(a -> a[0], TreeMap::new, Collectors.mapping(a -> a[1].split(" "), Collectors.toList())));

我有两个问题。

  1. 首先是TreeMap::new可能不起作用,因为顺序与原来的List不同。
  2. 其次,我似乎没有找到将List&lt;String[]&gt; 变成List&lt;String&gt; 的方法。

有什么想法吗?

【问题讨论】:

  • TreeMap 按键排序项。你想使用LinkedHashMap
  • 正是 ernest_k 所说的。你不想在这里排序。你想保持秩序

标签: java java-stream


【解决方案1】:

您想使用LinkedHashMap 来保留原始订单。所以你的代码应该是这样的:

Map<String, List<String>> collect = customList.stream()
    .map(s -> s.split(" +"))
    .collect(Collectors.toMap(a -> a[0], a -> Arrays.asList(a)
        .subList(1, a.length), (a, b) -> a, LinkedHashMap::new));

如果您的密钥不是唯一的,您可以使用类似这样的分组收集器(Collectors.flatMapping 需要 Java 9+):

collect = customList.stream()
    .map(s -> Arrays.asList(s.split(" +")))
    .collect(Collectors.groupingBy(l -> l.get(0), 
        LinkedHashMap::new, 
        Collectors.flatMapping(l -> l.stream().skip(1), Collectors.toList())));

【讨论】:

  • 这会产生预期的输出。
  • 另外,如果原始列表可能包含冲突,那么 (a, b) -&gt; a 可能是一个列表合并函数(不要忘记 Arrays.asList() 返回一个不可扩展的列表)。
  • @Thomas 是的,我假设密钥是唯一的。否则,正如 Arvind 所建议的那样,groupBy 是这样做的方法(或者当然是 toMap 中的适当合并函数)。
  • 请注意,使用合并功能(第三个参数Collectors.toMap() 采用)您也会这样做。 Collectors.groupingBy() 实际上是向下游移动,即 toList() 收集器会处理这个问题。
  • @ernest_k 这似乎是一个可以理解的理由,它也暗示你认为它确实很重要。 ;-) 我可以想象,对于大型输入字符串,您确实 不想 想要拆分两次。
【解决方案2】:

又一次更新:

此更新是为了满足 OP 在答案下方的评论中提到的以下要求:

我希望每个单词都作为列表中的一个单独元素。跟你的 解决方案,所有元素都在同一个 List 条目中。例如,我 想要 10000=[撒哈拉以南、非洲和澳大利亚。]

为了达到这个目的,你不应该拆分字符串。

演示:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> customList = Arrays.asList(
                   "5000  Buruli ulcer is an infectious disease",
                   "6000  characterized by the development",
                   "7000  of painless open wounds.",
                   "8000  The disease largely occurs in",
                   "10000  sub-Saharan Africa and Australia."
                );
        
        TreeMap<String, List<String>> collect = customList.stream().map(s -> s.split("  ", 2))
                .collect(Collectors.groupingBy(a -> a[0],
                        () -> new TreeMap<String, List<String>>(Comparator.comparingInt(Integer::parseInt)),
                        Collectors.mapping(a -> a[1], Collectors.toList())));
        
        System.out.println(collect);
    }
}

输出:

{5000=[Buruli ulcer is an infectious disease], 6000=[characterized by the development], 7000=[of painless open wounds.], 8000=[The disease largely occurs in], 10000=[sub-Saharan Africa and Australia.]}

或者根据我原来的答案:

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> customList = Arrays.asList(
                   "5000  Buruli ulcer is an infectious disease",
                   "6000  characterized by the development",
                   "7000  of painless open wounds.",
                   "8000  The disease largely occurs in",
                   "10000  sub-Saharan Africa and Australia."
                );

        Map<String, List<String>> collect = customList.stream().map(s -> s.split("\\s+", 2))
                .collect(Collectors.groupingBy(a -> a[0], TreeMap::new,
                        Collectors.mapping(a -> a[1], Collectors.toList())));

        System.out.println(collect);
    }
}

输出:

{10000=[sub-Saharan Africa and Australia.], 5000=[Buruli ulcer is an infectious disease], 6000=[characterized by the development], 7000=[of painless open wounds.], 8000=[The disease largely occurs in]}

Aniket提出的解决方案:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> customList = Arrays.asList(
                   "5000  Buruli ulcer is an infectious disease",
                   "6000  characterized by the development",
                   "7000  of painless open wounds.",
                   "8000  The disease largely occurs in",
                   "10000  sub-Saharan Africa and Australia."
                );
        
        TreeMap<String, List<String>> collect = customList.stream().map(s -> s.split("  ", 2))
                .collect(Collectors.groupingBy(a -> a[0],
                        () -> new TreeMap<String, List<String>>(Comparator.comparingInt(Integer::parseInt)),
                        Collectors.mapping(a -> Arrays.toString(a[1].split(" ")), Collectors.toList())));

        System.out.println(collect);
    }
}

输出:

{5000=[[Buruli, ulcer, is, an, infectious, disease]], 6000=[[characterized, by, the, development]], 7000=[[of, painless, open, wounds.]], 8000=[[The, disease, largely, occurs, in]], 10000=[[sub-Saharan, Africa, and, Australia.]]}

原答案:

你快到了。你可以这样做:

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> customList = Arrays.asList(
                   "5000  Buruli ulcer is an infectious disease",
                   "6000  characterized by the development",
                   "7000  of painless open wounds.",
                   "8000  The disease largely occurs in",
                   "10000  sub-Saharan Africa and Australia."
                );

        Map<Object, List<Object>> collect = customList.stream().map(s -> s.split("\\s+", 2))
                .collect(Collectors.groupingBy(a -> a[0], TreeMap::new,
                        Collectors.mapping(a -> Arrays.asList(a[1].split("\\s+")), Collectors.toList())));

        System.out.println(collect);
    }
}

输出:

{10000=[[sub-Saharan, Africa, and, Australia.]], 5000=[[Buruli, ulcer, is, an, infectious, disease]], 6000=[[characterized, by, the, development]], 7000=[[of, painless, open, wounds.]], 8000=[[The, disease, largely, occurs, in]]}

【讨论】:

  • 如果知道密钥是唯一的,使用groupBy 代替toMap 有什么好处吗?
  • @ThanosM - 已经有很多很好的答案可以描述这两者之间的区别,例如你可以查看this
  • 我希望每个单词在List 中作为一个单独的元素。使用您的解决方案,所有元素都在同一个 List 条目中。比如我想10000=[sub-Saharan, Africa, and, Australia.]
  • @ThanosM - 我出去吃午饭了。刚刚看到您的评论并发布了更新作为此要求的解决方案。如果有任何进一步的疑问/问题,请随时发表评论。
猜你喜欢
  • 2021-09-17
  • 1970-01-01
  • 2018-07-16
  • 1970-01-01
  • 1970-01-01
  • 2018-09-06
  • 2022-01-11
  • 1970-01-01
  • 2021-01-13
相关资源
最近更新 更多