如何使用 Java 8 个流从列表中过滤元素?

How to filter element from a List using Java 8 streams?

我有一个使用 filter() 操作的简单 Java 8 示例。

当我尝试过滤时,得到的是一个空列表。

class Per {
    String id;

    Per(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }
    public static void main(String[] args) {

        List<String> allIds = new ArrayList<>();
        allIds.add("1");
        allIds.add("2");
        allIds.add("3");
        allIds.add("4");

        List<Per> unavailableItems = new ArrayList<>();
        unavailableItems.add(new Per("2"));
        unavailableItems.add(new Per("4"));

        List<Per> ids = unavailableItems.stream().filter(per -> !(allIds.contains(per.getId())))
                .collect(Collectors.toList());
        System.out.println(ids);// why its returning empty value


    }
}

为什么得到一个空列表?

您得到一个空列表,因为操作 filter() 将只在流中保留与给定谓词匹配的元素。

Returns a stream consisting of the elements of this stream that match the given predicate.

由于源列表中的所有值都存在于 allIds 列表谓词中,因此始终 returns false.

如果您只想保留 allIds 列表中存在的元素,请将谓词更改为:

filter(per -> allIds.contains(per.getId()))

为了降低 contains() 检查的时间复杂度,您还可以将 allIds 设为 HashSet


如果您想进行相反的检查 - 查找可用 id 的列表(id 来自 allIds 列表但不存在于 unavailableItems ) 你可以这样做:

List<String> availableId = allIds.stream()
    .filter(id -> unavailableItems.stream()
                                  .noneMatch(pers -> pers.getId() == id))
    .collect(Collectors.toList());

如果这就是您要实现的目标,我的建议是通过生成 Map<String, Per> - person by id 来改进上面的代码availableId 列表。并检查地图中是否不存在 id 而不是遍历整个 availableId 列表。

Map<String, Per> personById = unavailableItems.stream()
    .collect(Collectors.toMap(Per::getId, Function.identity()));
    
List<String> availableId = allIds.stream()
    .filter(id -> !personById.containsKey(id))
    .collect(Collectors.toList());

你的过滤条件前面有一个不是的符号:

!(allIds.contains(per.getId()))

因此,您不是过滤列表内的项目,而是过滤列表外的项目。您 unavailibleItems 列表仅包含在 allIds 列表中具有匹配项目的项目,这意味着这种情况永远不会成立。但是,如果您删除了非符号,它会起作用,或者如果您将项目添加到 unavailibleItems 列表中,但在 allId 的列表中没有匹配值,它会起作用。

filter为:

“我是否应该包括(过滤)这个项目,是或否”

因此,当您的代码执行以下操作时:

List<Per> ids = unavailableItems.stream().filter(per -> !(allIds.contains(per.getId())))

然后你的代码检查它是否应该删除项目 Per("2")

逻辑是“如果allIds有一个"2"?那么没有,不过滤它,不包括它(那是您代码中的 !)

事实上,你没有包括它。

然后用 Per("4") 重复,你也不包含它。

不包括这两个元素的结果是一个空列表。

我希望这有助于澄清它。

您可能认为“filter”的意思是“过滤掉”或“排除”,但恰恰相反。

在我看来,您正试图做相反的事情,从 allIds 中过滤 unavailableItems

中的那些元素

在那种情况下:

    List<String> allAvailable = allIds.stream().filter( id -> {
        for (Per p : unavailableItems) {
          if ( p.getId().equals(id)) {
            return false;
          }
        }
        return true;

    }).collect(Collectors.toList());

    System.out.println(allAvailable);

完全满足您的需求并打印:

[1, 3]