【问题标题】:Check if an arraylist contains two strings检查数组列表是否包含两个字符串
【发布时间】:2025-11-26 18:20:09
【问题描述】:

我有一个像下面这样的 pojo 类

public CategoryModel {

public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

}

我创建了一个如下所示的数组列表。

List<CategoryModel> variantCategoryModelList = new ArrayList<>();
CategoryModel cat1= new CategoryModel();
cat1.setName(TEST1);
CategoryModel cat2= new CategoryModel();
cat2.setName(TEST1);
list.add(cat1);
list.add(cat2);

我必须检查值“TEST1”和“TEST2”是否存在于列表中,如果两个值都存在于“列表”中,则返回“true”,我尝试了类似下面的方法,尽管我的“列表”有这两个值,它返回 false。你能帮我检查一下我做错了什么,顺便说一句我使用的是 JDK 11。

final Optional<CategoryModel> optionalData = variantCategoryModelList.stream().
                filter(valueData -> TEST1.equalsIgnoreCase(valueData.getName())
                        && TEST2.equalsIgnoreCase(valueData.getName())).findFirst();

        if(optionalData.isPresent()){
            return true;
        }

【问题讨论】:

  • 您的第一段代码没有意义。第二段代码中的 TEST1 和 TEST2 是什么?
  • 如果list.get(0).getName 是你需要() 的方法,如果它是一个变量,恕我直言,它的名字很糟糕,因为它把我们与getter 和setter 混淆了。
  • 我不熟悉过滤器,但我很确定在这里,您检查 valueData.getName() 是否同时等于 TEST1TEST2,这似乎并不你想要的都不可能。
  • 嗨菲利普,现在已经更新了我的代码。是的,我在使用流函数时可能是错误的,但我希望在该列表中检查这两个字符串并返回 true 如果两者都存在于列表中.
  • 这两行应该有错误list.get(0).getName ="TEST1"; list.get(1).getName ="TEST2";,你想解决这个问题吗?

标签: java for-loop java-stream


【解决方案1】:

这是一种方法:

Set<String> set = Set.of("TEST1", "TEST2");
boolean result = list.stream()
    .filter(cat -> set.contains(cat.getName().toUpperCase())
    .distinct()
    .limit(2)
    .count() == 2L;

这会流式传输类别列表,然后仅保留名称为 TEST1TEST2 的类别。然后,我们删除重复项并在找到两个(已经不同的)类别名称后停止。这确保了短路。最后,我们检查最后是否正好有两个元素。

【讨论】:

    【解决方案2】:

    Set 将是一种更自然(更快)的数据结构:

    return variantCategoryModelList.stream()
                           .map(CategoryModel::getName)
                           .collect(Collectors.toSet())
                           .containsAll(Set.of("TEST1", "TEST2"));
    

    您的问题是 and (&amp;&amp;) 而不是 or

    所以:

    Set<String> soughtNames = Set.of("TEST1", "TEST2");
    return variantCategoryModelList.stream()
                           .filter(cm -> soughtNames.contains(cm.getName()))
                           .distinct()
                           .count() == 2L;
    

    正如@fps 评论的那样,列表中需要distinct() 以防止["Test1", "Test1"] 被接受,或["Test1", "Test1", "Test2"] 失败。

    这显然是低效的 - 找到了 2 个条目 - 仍然走到最后。

    你想要:

    Set<String> soughtNames = Set.of("TEST1", "TEST2");
    return soughtNames.stream()
        .allMatch(soughtName ->
             variantCategoryModelList.stream()
                 .anyMatch(cm -> soughtName.equals(cm.getName()));
    

    或者有点复古风格:

    return
        variantCategoryModelList.stream()
                 .anyMatch(cm -> "TEST1".equals(cm.getName())) &&
        variantCategoryModelList.stream()
                 .anyMatch(cm -> "TEST2".equals(cm.getName()));
    

    【讨论】:

    • @fps 非常正确,普通的 List 是行不通的。已添加distinct()
    【解决方案3】:

    您可以将您的 CategoryModel 映射到名称并收集到字符串列表并调用 List.containsAll

    return variantCategoryModelList.stream()
                                   .map(CategoryModel::getName)
                                   .collect(Collectors.toList())
                                   .containsAll(Arrays.asList("TEST1","TEST2"));
    

    【讨论】:

    • 不错的解决方案;我唯一建议的是 OP 希望它不区分大小写,因此添加另一个 .map(String::toUpperCase) 以使所有内容都大写
    • 最好使用Set
    • 此外,当 TEST1 和 TEST2 都已找到时,此解决方案不会短路
    • @fps 短路,相反:return Stream.of("TEST1","TEST2") .allMatch(s -&gt; variantCategoryModelList.stream().anyMatch(m -&gt; m.getName().equalsIgnoreCase(s))); 我想说,它更简单。
    • 本来打算根据 cmets 编辑我的答案,但由于@JoppEggen 写了一个更详细和更好的答案,我保持原样并建议 OP 接受 JoppEggen 的答案。