简体   繁体   English

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

[英]How to remove multiple elements from Set/Map AND knowing which ones were removed?

I have a method that has to remove any element listed in a (small) Set<K> keysToRemove from some (potentially large) Map<K,V> from . 我有一个方法,必须删除(小) Set<K> keysToRemove列出的任何元素从一些(可能很大) Map<K,V> from But removeAll() doesn't do, as I need to return all keys that were actually removed, since the map might or might not contain keys that require removal. 但是removeAll()不能这样做,因为我需要返回实际删除的所有键,因为map可能包含也可能不包含需要删除的键。

Old-school code is straight forward: 老派代码很简单:

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;
}

The same, written using streams: 同样,使用流编写:

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

I find that a bit more concise, but I also find that lambda too clunky. 我发现它更简洁,但我也发现lambda太笨重了。

Any suggestions how to achieve the same result in less clumsy ways? 有什么建议如何以不太笨拙的方式达到相同的结果?

The “old-school code” should rather be 应该是“老派代码”

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

Since you said that keysToRemove is rather small, the copying overhead likely doesn't matter. 既然你说keysToRemove相当小,那么复制开销可能并不重要。 Otherwise, use the loop, but don't do the hash lookup twice: 否则,请使用循环,但不要执行两次哈希查找:

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;
}

You can express the same logic as a stream as 您可以表示与流相同的逻辑

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

but since this is a stateful filter, it is highly discouraged. 但由于这是一个有状态的过滤器,因此非常气馁。 A cleaner variant would be 一个更清洁的变体将是

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;
}

and if you want to maximize the “streamy” usage, you can replace from.keySet().removeAll(result); 如果你想最大化“流”用法,你可以替换from.keySet().removeAll(result); with from.keySet().removeIf(result::contains) , which is quiet expensive, as it is iterating over the larger map, or with result.forEach(from.keySet()::remove) , which doesn't have that disadvantage, but still, isn't more readable than removeAll . with from.keySet().removeIf(result::contains) ,这是安静的,因为它迭代更大的地图,或者使用result.forEach(from.keySet()::remove) ,它没有这个缺点,但仍然不如removeAll可读。

All in all, the “old-school code” is much better than that. 总而言之,“老派代码”要好得多。

More concise solution, but still with unwanted side effect in the filter call: 更简洁的解决方案,但在filter调用中仍有不必要的副作用

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

Set.remove already returns true if the set contained the specified element. 如果set包含指定的元素,则Set.remove已经返回true

PS In the end, I would probably stick with the "old-school code". PS最后,我可能会坚持使用“老派代码”。

I wouldn't use Streams for this. 我不会为此使用Streams。 I would take advantage of retainAll : 我会利用retainAll

public Set<K> removeEntries(Map<K, V> from) {
    Set<K> matchingKeys = new HashSet<>(from.keySet());
    matchingKeys.retainAll(keysToRemove);

    from.keySet().removeAll(matchingKeys);

    return matchingKeys;
}

You can use the stream and the removeAll 您可以使用流和removeAll

Set<K> fromKeys = from.keySet();
Set<K> removedKeys = keysToRemove.stream()
    .filter(fromKeys::contains)
    .collect(Collectors.toSet());
fromKeys.removeAll(removedKeys);
return removedKeys;

You can use this: 你可以用这个:

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

It's similar to Oleksandr 's answer, but avoiding the side effect. 它类似于Oleksandr的回答,但避免了副作用。 But I would stick with that answer, if you are looking for performance. 但如果你正在寻找表现,我会坚持这个答案。

Alternatively you could use Stream.peek() for the remove, but be careful with other side effects (see the comments). 或者,您可以使用Stream.peek()进行删除,但要注意其他副作用(请参阅注释)。 So I would not recommend that. 所以我不建议这样做。

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

To add another variant to the approaches, one could also partition the keys and return the required Set as: 要为方法添加另一个变体,还可以对键进行分区并将所需的Set返回为:

public Set<K> removeEntries(Map<K, ?> from) {
    Map<Boolean, Set<K>> partitioned = keysToRemove.stream()
            .collect(Collectors.partitioningBy(k -> from.keySet().remove(k),
                    Collectors.toSet()));
    return partitioned.get(Boolean.TRUE);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在自定义OrderedLinkedList类中显示从列表中删除的元素? - How can I display which elements were removed from list in custom OrderedLinkedList class? 如何从ListChangeListener.Change知道哪些元素已被删除? - How to know from ListChangeListener.Change about which elements were removed? 从数组中删除3的倍数或其中包含数字3的所有元素。 例如13和15都将被删除 - Remove all elements from the array which are either multiple of 3 or have the digit 3 in them. For e.g. 13 and 15 will be both removed 使用地图和过滤器功能从树图中存在的集合中删除元素(Java集合) - Use map and filter functions to remove elements from a set which exists in a treemap (Java Collections) 如何从 ListIterator 中删除所有元素,然后添加新元素 - How to remove all elements from a ListIterator and then add new ones 无法从地图的条目集中删除元素 - Unable to remove elements from Map's entry set 如何从另一个地图中删除一个地图的元素? - How to remove elements of one map from another map? 比较两个迭代器,并检查两个元素之间添加,删除或相同的元素 - Comparing two iterators and checking which elements were added, removed or the same between the two 如何从 map 中的集合中删除元素 - How to remove an element from a set that is in a map 如何创建一个 Java 列表/映射/集合,我可以在其中修改值但不能添加或删除元素? - How can I create a Java List/Map/Collection in which I can modify the values but not add nor remove elements from it?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM