我有一个方法必须删除(小)Set<K> keysToRemove
中列出的任何元素来自一些(可能很大)Map<K,V> from
.但是removeAll()
不这样做,因为我需要返回所有实际删除的键,因为 map 可能包含也可能不包含需要删除的键。
老派代码直截了当:
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 太笨重了。
有什么建议可以以不那么笨拙的方式实现相同的结果吗?
最佳答案
“老派代码”应该是
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)
,它非常昂贵,因为它正在迭代更大的 map ,或者使用 result.forEach(from.keySet()::remove)
,它没有但是,这个缺点并不比 removeAll
更具可读性。
总而言之,“老派代码”要好得多。
关于java - 如何从 Set/Map 中删除多个元素并知道哪些元素被删除了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56582681/