【问题标题】:How to search element in a List?如何在列表中搜索元素?
【发布时间】:2019-04-23 16:10:28
【问题描述】:

我有两个列表,例如:

列表 1:只有一个元素

List<String> ids=new ArrayList<String>();

列表 2:有 1000 个对象

List<ABC> abc=  new ArrayList<ABC>();

a.matIDS

注意:matIDS 是字符串集合(例如:abc,def,ghi)

for(ABC a : abc){
    for(String id : a.matIDs()){
        if(ids.contains(id)){
            LOG.info("ID found:::");
        }else{
            LOG.info("ID NOT found:::");
        }
    }
}

问题:

在列表 1 中只有 1 个元素,而在列表 2 中有 1000 个元素。 我是否需要检查所有这 1000 个元素才能找到第一个元素?

有没有更好的办法?

【问题讨论】:

  • 也许你应该在找到匹配项时break
  • 在目前的情况下,在一次性场景中只寻找一个元素,O(n) 是你所希望的最好的。您可以通过多线程或提前做一些事情来加速它(在构建列表时创建某种查找表),但对于单个用例,它仍然是 O(n)。如果您多次使用它,那么还有更有效的方法
  • 你所说的“更好的方法”到底是什么意思?您是在寻求一种更优雅的搜索方式,还是专门追求性能改进?如果它是性能改进,那么答案可能是:如果仅对 1000 个对象进行不常见的搜索,请不要打扰

标签: java list search arraylist collections


【解决方案1】:

您可以优化现有代码以在找到匹配项时退出循环。

booean isFound=false;
for(ABC a : abc){
    for(String id : a.matIDs()){
        if(ids.contains(id)){
            isFound=true;
            break;
        }
    }
    if(isFound)
        break;
}
if(isFound)
    LOG.info("ID found:::");
else
    LOG.info("ID NOT found:::");

您也可以使用流,

boolean isFound=abc.stream().flatMap(e-> e.matIDS.stream()).anyMatch(ids::contains);
if(isFound)
    LOG.info("ID found:::");
else
    LOG.info("ID NOT found:::");

要查找匹配的元素,您可以使用filter 并收集到Set

  Set<String>  matchedElements=abc.stream()
                .flatMap(e-> e.matIDS.stream())
                .filter(ids::contains)
                .collect(Collectors.toSet());

希望对你有帮助。

【讨论】:

  • 这会涵盖所有场景,而不仅仅是单个元素吗?
  • 它将涵盖您提到的场景。如果还有其他情况,请告诉我。
  • 我的意思是这种情况只检查 1 个元素,还是检查任意数量的元素,如 1 或 2 或 3 ...n 等?同样在第二个方法中,我可以知道如何在控制台上打印找到的元素吗?
  • 它将检查任意数量的元素。假设您在列表 1 中有 3 个元素 a,b,c 即 ids ,如果列表 2 中存在 3 个元素中的任何一个,它的代码将返回 true。希望它对您有所帮助。
  • 另外在第二种方法中,我可以知道如何在控制台上打印找到的元素吗?
【解决方案2】:

如果你所说的“更好”是指更清晰,那么也许你可以考虑使用流:

abc.stream().flatMap(ABC::matIDs).anyMatch(ids::contains);

您是否认为这“更好”取决于您所追求的。

如果您经常检查某个特定 ID 是否在列表中,那么您可以将这些 ID 收集到一个集合中:

Set<String> abcIDs = abc.stream().flatMap(ABC::matIDs).collect(toSet());

然后检查特定字符串是否在集合中很简单,无需返回原始列表。

【讨论】:

    【解决方案3】:

    如果您确实需要在一个列表中针对另一个集合快速查找一个(或多个值),那么最快的数据结构可能是针对一个集合进行搜索:

    Set<String> set = new HashSet<>(abc);
    

    然后,您可以迭代第一个列表并在另一个集合中以恒定时间查找每个条目:

    for (String id : ids) {
        if (set.contains(id)) {
            LOG.info("ID found:::");
        }
        else {
            LOG.info("ID NOT found:::");
        }
    }
    

    这是对您当前的蛮力方法的改进,即O(n*m),其中nmidsabc 列表的大小。现在,运行时间只是O(m)ids 列表的大小。

    【讨论】:

    • 默认使用什么,HashMap?
    • @phflack 好问题,这是我的猜测。 Here 是一个 SO 问题,讨论如何生成 TreeMap,它不是默认行为。
    • Collectors.toMap(),貌似用了HashMap,调用return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
    • @racraman 我存储了一个null 值,所以我希望性能方面我的解决方案与使用HashSet 大致相同。
    • toMap 在空值上失败 bugs.openjdk.java.net/browse/JDK-8148463
    猜你喜欢
    • 2011-06-03
    • 1970-01-01
    • 1970-01-01
    • 2018-12-10
    • 1970-01-01
    • 2016-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多