在Java中通过值从Map中删除元素的最快方法是什么?

时间:2009-08-26 16:23:24

标签: java performance collections

在Java中按值从Map中删除元素的最快方法是什么?

目前我正在使用:

    DomainObj valueToRemove = new DomainObj();
    String removalKey = null;

    for (Map.Entry<String, DomainObj> entry : map.entrySet()) {
        if (valueToRemove.equals(entry.getValue())) {
            removalKey = entry.getKey();
            break;
        }
    }

    if (removalKey != null) {
        map.remove(removalKey);
    }

12 个答案:

答案 0 :(得分:70)

正确而快速的单线实际上是:

while (map.values().remove(valueObject));

有点奇怪,上面的大多数例子都假设valueObject是唯一的。

答案 1 :(得分:26)

如果不使用双向地图(commons-collectionsgoogle collections拥有它们),您就会陷入迭代地图

答案 2 :(得分:24)

以下是单行解决方案:

map.values().remove(valueToRemove);

这可能比定义自己的迭代器更快,因为JDK集合代码已经过显着优化。

正如其他人所提到的,bimap将有更快的值删除,但它需要更多的内存并且需要更长的时间来填充。此外,bimap仅在值唯一时才有效,这可能是您的代码中的情况,也可能不是。

答案 3 :(得分:11)

如果你没有反向地图,我会选择迭代器。

DomainObj valueToRemove = new DomainObj();

for (
    Iterator<Map.Entry<String, DomainObj>> iter = map.entrySet().iterator();
    iter.hasNext();
) {
    Map.Entry<String, DomainObj> entry = iter.next();
    if (valueToRemove.equals(entry.getValue())) {
        iter.remove();
        break; // if only want to remove first match.
    }
}

答案 4 :(得分:11)

map.values().removeAll(Collections.singleton(null));

引用How to filter "Null" values from HashMap<String, String>?,我们可以对java 8执行以下操作:

map.values().removeIf(valueToRemove::equals);

答案 5 :(得分:7)

您始终可以使用值集合,因为对该集合所做的任何更改都将导致更改反映在地图中。因此,如果您要调用Map.values()。remove(valueToRemove)应该可以工作 - 虽然我不确定您是否会看到比使用该循环更好的性能。一个想法是扩展或覆盖map类,这样后备集合总是按值排序 - 这将使您能够对值更快的二进制搜索。

编辑:这与Alcon的答案基本相同,但我不认为他会工作,因为entrySet仍然按键排序 - 在这种情况下你不能用值调用.remove()。

这也假设该值应该是唯一的,或者您也希望从Map中删除任何重复项。

答案 6 :(得分:6)

我会用这个

 Map x = new HashMap();
x.put(1, "value1");
x.put(2, "value2");
x.put(3, "value3");
x.put(4, "value4");
x.put(5, "value5");
x.put(6, "value6");

x.values().remove("value4");

编辑:    因为对象是由“指针”引用而不是由值引用。

<磷>氮

答案 7 :(得分:4)

如果您无法从DomainObj中找出密钥,那么我不知道如何改进它。没有内置方法从值中获取密钥,因此您遍历地图。

如果这是你一直在做的事情,你可能会保留两张地图(string-&gt; DomainObj和DomainObj-&gt; Key)。

答案 8 :(得分:2)

与大多数其他海报所说的一样,它通常是一个O(N)操作,因为你将不得不查看整个哈希表值列表。 @tackline有一个正确的解决方案,可以将内存使用量保持在O(1)(我给了他一个投票权)。

你的另一个选择是为了速度而牺牲记忆空间。如果您的地图大小合理,您可以并行存储两张地图。

如果你有一张地图,那么就与它保持平行。在一个地图上插入/删除时,也可以在另一个地图上执行。虽然这很丑陋,因为你浪费了空间,你必须确保DomainObj的“hashCode”方法写得正确,但你的删除时间从O(N)下降到O(1),因为你可以查找密钥/ object在任意方向上以恒定时间映射。

通常不是最好的解决方案,但如果您关注的是速度问题,我认为这可能与您将获得的速度一样快。

==================== 附录:这基本上是@msaeed所建议的,只是没有第三方库。

答案 9 :(得分:2)

迭代器的较短用法是使用values()迭代器。

DomainObj valueToRemove = new DomainObj();
for (Iterator<DomainObj> it = map.values().iterator(); it.hasNext();)) {
        if (valueToRemove.equals(it.next())) {
                it.remove();
                break;
        }
}

答案 10 :(得分:1)

我们知道这种情况很少出现,但非常有帮助。我更喜欢org.apache.commons.collectionsBidiMap

答案 11 :(得分:0)

我不认为这会在您的应用的生命周期中发生一次。

所以我要做的是委托另一个对象负责维护对添加到该地图的对象的引用。

所以下次你需要删除它时,你会使用“反向地图”...

 class MapHolder { 

     private Map<String, DomainObj> originalMap;
     private Map<DomainObj,String> reverseMap;

     public void remove( DomainObj value ) {
           if ( reverseMap.contains( value ) ) { 
                 originalMap.remove( reverseMap.get( value ) );
                 reverseMap.remove( value );
           }
     }
  }

这比迭代要快得多。

显然你需要让它们保持同步。但是如果你重新编写代码以使一个对象负责地图的状态,那就不应该那么难了。

请记住,在OOP中,我们拥有具有状态和行为的对象。如果您的数据遍布整个变量,那么您将在对象之间创建不必要的依赖关系

是的,您需要一些时间来更正代码,但是纠正它的时间将在未来为您节省很多麻烦。想一想。