【问题标题】:Handle a list of String处理字符串列表
【发布时间】:2023-02-26 09:05:03
【问题描述】:

我有一个字符串列表,我需要找到所有重复项,如果父母在列表中,还需要删除所有孩子。 我的字符串最多可以有 4 个级别: Level0:“根”,或留空 Level1:“瑞典”、“美国”等... Level2:“瑞典.斯德哥尔摩”、“美国.芝加哥”等... Level3:“瑞典.斯德哥尔摩.索尔纳”、“美国.芝加哥.西塞罗”等...

因此,如果我有一个列表["Sweden", "Sweden", "Sweden.Stockholm.Solna", "America.Chicago", "America.Chicago", "America.Chicago.Cicero"] ,那么只应返回["Sweden", "America.Chicago"]

如何使用 Java,最好是 java 8 stream API 来做到这一点?

以下 2 种方法可用于检查一个字符串是另一个字符串的父项还是子项:

private boolean isChildOf(String parentPath, String path) {
   return path.startsWith(parentPath.isBlank() ? parentPath : parentPath + ".") && !path.equals(parentPath);
}
private boolean isParentOf(String childPath, String path) {
   return childPath.startsWith(path + ".") && !path.equals(childPath);
}

【问题讨论】:

  • 条目Sweden.Stockholm.Solna 是否有可能出现在Sweden 之后?
  • 是的,字符串列表可以按任何顺序排列。
  • 如果其中一个副本是另一个副本的孩子(或父母)会怎样?如果Sweden.Stockholm.Solna在原始列表中出现两次,那么生成的列表是否只包含America.Chicago
  • 我对 OP 要求的理解是它只返回最上面的父级,不管是否有重复项。但是,结果列表不应包含重复项。
  • 不,即使 Sweden.Stockholm.Solna 在原始列表中出现两次,结果仍然应该是 ["Sweden", "America.Chicago"]

标签: java string list loops java-stream


【解决方案1】:

这是我测试过并按预期工作的实现。

//initialize list from example
List<String> nodes = Arrays.asList(new String[] {"Sweden", "Sweden", "Sweden.Stockholm.Solna", "America.Chicago", "America.Chicago", "America.Chicago.Cicero"});
//sort into map grouped by the root level of the entries (regardless of if the "root" exists in list or not)
Map<String, List<String>> groups = nodes.stream().collect(Collectors.groupingBy(s -> isRoot(s) ? s : s.substring(0, s.indexOf(".")), Collectors.toList()));
//since they are already sorted by the root, we can just return the shortest string in each list
List<String> parents = groups.entrySet().stream().map(e -> getParent(e.getValue())).collect(Collectors.toList());
System.out.println(parents);

这是我使用的自定义方法

//return the shortest String in the list
private String getParent(List<String> nodes) {
    return nodes.stream().min(Comparator.comparingInt(s -> s.length())).get();
}

private boolean isRoot(String s) {
    return !s.contains(".");
}

【讨论】:

  • 我最喜欢这个解决方案,谢谢!
  • 对于 f.c, f.c.e, f.e.f.ef, f.f, f.f.cd.abc, f.f.de,您的结果是 f.c。不应该是f.c, f.f, f.e.f.e吗?
【解决方案2】:

如果 isChildOf 方法正确,嵌套循环检查每个元素与列表中的每个其他元素(当前元素除外)应该可以解决问题。

首先删除重复项

Set<String> set = new HashSet<>(list);
list.clear();
list.addAll(set);

然后删除孩子

for(string child : list) {
    for(string parent : list) {
       if (isChildOf(Parent, Child)):
          list.remove(Child)
    }
}

【讨论】:

  • 我可能误解了 OP 的要求,但据我所知,结果列表应首先包含仅有的重复,所以如果原始列表包含重复,那么结果列表应该是一个空列表,我没有看到你的解决方案实现了这一点。
【解决方案3】:

创建一个新列表以获取所有重复项

Set<String> set = new HashSet<String>();
List<String> duplicates = new ArrayList<String>();
list.forEach(word -> {
            
    if(!set.add(word))
        duplicates.add(word);

});

创建另一个列表以获取所有孩子

List<String> childs = new ArrayList<String>();
for(String child : duplicates) {
    for(String parent : duplicates) {
       if (isChildOf(parent, child))
           childs.add(child);
    }
}

从重复项中删除所有子项

duplicates.removeAll(childs);

Final duplicates 是您想要的最终结果。

【讨论】:

    【解决方案4】:

    这是使用集合的一种方法。这将适用于层次结构的任何深度。

    • 首先将列表添加到集合中。这会删除不需要的重复项。
    • 然后遍历该集合的副本并删除每个元素
      • 如果当前元素不等于被测元素
      • 并且当前元素以被测元素开始。
    List<String> hierarchy = List.of(
           "Sweden.Stockholm.Solna", "America.Chicago", "America.Chicago",
           "America.Chicago.Cicero", "Sweden.Stockholm.Solna","Sweden", "Sweden");
    
    Set<String> set = nodes.stream()
            .collect(Collectors.toCollection(HashSet::new));
    
    for (String str : new HashSet<>(set)) {
        set.removeIf(v -> !v.equals(str) && v.startsWith(str));
    }
    
    set.forEach(System.out::println);
    

    印刷

    Sweden
    America.Chicago
    

    【讨论】:

      猜你喜欢
      • 2012-01-02
      • 2017-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多