Какой самый быстрый способ удалить элемент из карты по значению в Java? - PullRequest
32 голосов
/ 26 августа 2009

Какой самый быстрый способ удалить элемент с карты по значению в Java?

В настоящее время я использую:

    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 ]

70 голосов
/ 29 апреля 2010

Правильный и быстрый однострочник будет на самом деле:

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

Довольно странно, что в большинстве приведенных выше примеров предполагается, что valueObject уникален.

26 голосов
/ 26 августа 2009

Без использования двунаправленной карты ( commons-collection и google collection имеют их), вы застряли в итерации Map

24 голосов
/ 25 ноября 2009

Вот решение в одну строку:

map.values().remove(valueToRemove);

Это, вероятно, быстрее, чем определение собственного итератора, поскольку код коллекции JDK был значительно оптимизирован.

Как уже упоминалось, bimap будет быстрее удалять значения, хотя он требует больше памяти и занимает больше времени для заполнения. Кроме того, bimap работает только тогда, когда значения уникальны, что может или не может иметь место в вашем коде.

11 голосов
/ 28 июля 2014
map.values().removeAll(Collections.singleton(null));

ссылка на Как отфильтровать "нулевые" значения из HashMap ? , мы можем сделать следующее для Java 8:

map.values().removeIf(valueToRemove::equals);
11 голосов
/ 26 августа 2009

Если у вас нет обратной карты, я бы выбрал итератор.

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.
    }
}
7 голосов
/ 26 августа 2009

Вы всегда можете использовать коллекцию значений, так как любые изменения, внесенные в эту коллекцию, приведут к отражению изменения на карте. Поэтому, если бы вы вызвали Map.values ​​(). Remove (valueToRemove), это должно сработать - хотя я не уверен, что вы увидите производительность лучше, чем у вас с этим циклом. Одной из идей было бы расширить или переопределить класс карты таким образом, чтобы резервная коллекция всегда сортировалась по значению - это позволило бы вам выполнить двоичный поиск по значению, которое может быть быстрее.

Редактировать: По сути, это то же самое, что и ответ Алкона, за исключением того, что я не думаю, что он будет работать, так как entrySet все еще будет упорядочиваться по ключу - в этом случае вы не можете вызвать .remove () со значением.

Это также предполагает, что значение должно быть уникальным или что вы также хотите удалить любые дубликаты с карты.

6 голосов
/ 26 августа 2009

Я бы использовал это

 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");

редактировать: потому что на объекты ссылается «указатель», а не значение.

N

4 голосов
/ 26 августа 2009

Если у вас нет возможности выяснить ключ от DomainObj, то я не вижу, как вы можете улучшить это. Нет встроенного метода для получения ключа от значения, поэтому у вас есть для итерации по карте.

Если вы постоянно этим занимаетесь, вы можете сохранить две карты (string-> DomainObj и DomainObj-> Key).

2 голосов
/ 22 ноября 2009

Более короткое использование итератора - использование итератора values ​​().

DomainObj valueToRemove = new DomainObj();
for (Iterator<DomainObj> it = map.values().iterator(); it.hasNext();)) {
        if (valueToRemove.equals(it.next())) {
                it.remove();
                break;
        }
}
2 голосов
/ 26 августа 2009

Как говорилось в большинстве других постеров, это обычно операция O (N), потому что вам придется просматривать весь список значений хеш-таблиц независимо. У @tackline есть правильное решение для сохранения использования памяти на уровне O (1) (за него я проголосовал).

Другой вариант - пожертвовать памятью ради скорости. Если ваша карта имеет разумный размер, вы можете хранить две карты параллельно.

Если у вас есть Карта, тогда поддерживайте Карту параллельно с ней. Когда вы вставляете / удаляете на одной карте, делайте это и на другой. Конечно, это уродливее, потому что вы тратите место впустую, и вам нужно убедиться, что метод «hashCode» DomainObj написан правильно, но ваше время удаления падает с O (N) до O (1), потому что вы можете искать ключ / отображение объекта в постоянном времени в любом направлении.

Как правило, не самое лучшее решение, но если ваша проблема номер один - скорость, я думаю, что это, вероятно, так же быстро, как вы собираетесь.

==================== Приложение: По сути, это то, что @msaeed предложил без сторонней библиотеки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...