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

What's the quickest way to remove an element from a Map by value in Java?

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

目前,我正在使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
    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);
    }


正确和快速的一线实际上是:

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

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


无需使用双向地图(公共收藏夹和Google收藏夹都有它们),您就无法遍历地图


这是一线解决方案:

1
map.values().remove(valueToRemove);

这可能比定义自己的迭代器要快,因为JDK集合代码已得到了极大地优化。

正如其他人提到的那样,尽管bimap需要更多的内存并且需要更长的填充时间,但其移除值的速度更快。 另外,只有当值唯一时,bimap才起作用,这在代码中可能是,也可能不是。


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

参考如何从HashMap ?中过滤" Null"值,我们可以针对Java 8执行以下操作:

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


如果您没有反向映射,我会去找一个迭代器。

1
2
3
4
5
6
7
8
9
10
11
12
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.
    }
}


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

编辑:这基本上与爱尔康的答案相同,除了我认为他不会起作用,因为entrySet仍将通过键进行排序-在这种情况下,您无法使用该值调用.remove()。

这也假设该值应该是唯一的,或者您也想从Map中删除所有重复项。


我会用这个

1
2
3
4
5
6
7
8
9
 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");

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

?


如果您无法从DomainObj中找出密钥,那么我看不出您可以如何对此进行改进。没有内置方法可以从值中获取键,因此您必须遍历地图。

如果这是您一直在做的事情,则可以维护两个映射(string-> DomainObj和DomainObj-> Key)。


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

1
2
3
4
5
6
7
DomainObj valueToRemove = new DomainObj();
for (Iterator<DomainObj> it = map.values().iterator(); it.hasNext();)) {
        if (valueToRemove.equals(it.next())) {
                it.remove();
                break;
        }
}

就像大多数其他张贴者所说的那样,通常这是一个O(N)操作,因为无论如何,您都必须仔细查看整个哈希表值列表。 @tackline具有将内存使用率保持在O(1)的正确解决方案(为此我给了他投票)。

您的另一个选择是为了提高速度而牺牲内存空间。如果您的地图尺寸合理,则可以并行存储两个地图。

如果您有一个Map,请与它并行维护一个Map。当您在一张地图上插入/删除时,也要在另一张地图上进行插入/删除。因为这浪费了空间,并且必须确保正确编写DomainObj的" hashCode"方法,所以这样做很丑陋,但是删除时间从O(N)减少到O(1),因为您可以查找键/对象以恒定时间在任一方向上映射。

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

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


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


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

因此,我要做的就是将负责维护对添加到该映射的对象的引用的责任委托给另一个对象。

因此,下次需要删除它时,请使用该"反向映射" ...

1
2
3
4
5
6
7
8
9
10
11
12
 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中,我们具有具有状态和行为的对象。如果您的数据在各处传递变量,那么您将在对象之间创建不必要的依赖关系

是的,需要花费一些时间来纠正代码,但是花费在纠正代码上的时间将在将来为您省去很多麻烦。想一想。