如何从Set/Map中删除多个元素并知道哪些元素被删除了?

2022-09-01 11:40:38

我有一个方法,必须从一些(可能很大)中删除(小)中列出的任何元素。但不会,因为我需要返回实际删除的所有键,因为映射可能包含也可能不包含需要删除的键。Set<K> keysToRemoveMap<K,V> fromremoveAll()

老式代码很简单:

public Set<K> removeEntries(Map<K, V> from) {
    Set<K> fromKeys = from.keySet();
    Set<K> removedKeys = new HashSet<>();
    for (K keyToRemove : keysToRemove) {
        if (fromKeys.contains(keyToRemove)) {
            fromKeys.remove(keyToRemove);
            removedKeys.add(keyToRemove);
        }
    }
    return removedKeys;
}

同样的,使用流编写:

Set<K> fromKeys = from.keySet();
return keysToRemove.stream()
        .filter(fromKeys::contains)
        .map(k -> {
            fromKeys.remove(k);
            return k;
        })
        .collect(Collectors.toSet());

我发现这有点简洁,但我也发现lambda太笨拙了。

任何建议如何以不那么笨拙的方式达到相同的结果?


答案 1

“老派守则”应该是

public Set<K> removeEntries(Map<K, ?> from) {
    Set<K> fromKeys = from.keySet(), removedKeys = new HashSet<>(keysToRemove);
    removedKeys.retainAll(fromKeys);
    fromKeys.removeAll(removedKeys);
    return removedKeys;
}

既然你说这相当小,那么复制开销可能并不重要。否则,请使用循环,但不要执行哈希查找两次:keysToRemove

public Set<K> removeEntries(Map<K, ?> from) {
    Set<K> fromKeys = from.keySet();
    Set<K> removedKeys = new HashSet<>();
    for(K keyToRemove : keysToRemove)
        if(fromKeys.remove(keyToRemove)) removedKeys.add(keyToRemove);
    return removedKeys;
}

您可以将与流相同的逻辑表示为

public Set<K> removeEntries(Map<K, ?> from) {
    return keysToRemove.stream()
        .filter(from.keySet()::remove)
        .collect(Collectors.toSet());
}

但是由于这是一个有状态过滤器,因此非常不鼓励这样做。更清洁的变体将是

public Set<K> removeEntries(Map<K, ?> from) {
    Set<K> result = keysToRemove.stream()
        .filter(from.keySet()::contains)
        .collect(Collectors.toSet());
    from.keySet().removeAll(result);
    return result;
}

如果您想最大化“流”用法,则可以将其替换为 ,这是安静的昂贵,因为它正在迭代较大的地图,或者带有,它没有该缺点,但仍然不比.from.keySet().removeAll(result);from.keySet().removeIf(result::contains)result.forEach(from.keySet()::remove)removeAll

总而言之,“老派守则”远不止于此。


答案 2

更简洁的解决方案,但在通话中仍然有不必要的副作用filter

Set<K> removedKeys =
    keysToRemove.stream()
                .filter(fromKeys::remove)
                .collect(Collectors.toSet());

Set.remove 如果 包含指定的元素,则已返回。trueset

附言最后,我可能会坚持使用“老派代码”。


推荐