2009-08-26 9 views
28

Jaki jest najszybszy sposób usunięcia elementu z mapy według wartości w języku Java?Jaki jest najszybszy sposób usunięcia elementu z mapy według wartości w języku Java?

Obecnie używam:

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); 
    } 
+0

Używam HashMap Javy – Supertux

Odpowiedz

25

Bez użycia dwukierunkowego mapy (commons-collections i google collections je mieć), utkniesz z iteracji na mapie

+1

+1 zalecające zbiory google BiMap. Jest szybszy, ale ma dodatkowe obciążenie pamięci. – notnoop

+0

Albo możesz zachować dwie mapy, co robią te mapy. –

4

Jeśli nie masz drogę do wykryj klucz z DomainObj, wtedy nie widzę, jak możesz to poprawić. Nie ma wbudowanej metody, aby uzyskać klucz od wartości, więc musisz mieć do iteracji po mapie.

Jeśli to jest coś, co robisz przez cały czas, możesz zachować dwie mapy (string-> DomainObj i DomainObj-> Key).

+0

+1. Powinieneś znaleźć sposób na zapisanie wartości jako klucza, jeśli chcesz zoptymalizować. Jeśli nie możesz, nie masz wyboru: musisz powtórzyć. –

9

Jeśli nie masz odwrotnej mapy, wybrałbym iterator.

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

Uzgodnione. Jedyną rzeczą, o której chciałbym wspomnieć, jest to, że jeśli chcesz często usuwać według wartości, upewnij się, że mapa jest faktyczną strukturą danych, której powinieneś używać w swoim scenariuszu. –

7

Zawsze można użyć kolekcji wartości, ponieważ wszelkie zmiany dokonane w tej kolekcji spowodują, że zmiana zostanie odzwierciedlona na mapie. Jeśli więc chcesz wywołać funkcję Map.values ​​(). Remove (valueToRemove), która powinna działać - chociaż nie jestem pewien, czy wydajność będzie lepsza niż w tej pętli. Jednym z pomysłów byłoby rozszerzenie lub przesłonięcie klasy mapy tak, aby kolekcja bazowa była zawsze sortowana według wartości - która umożliwiłaby wyszukiwanie binarne wartości, która może być szybsza.

Edycja: Zasadniczo jest to ta sama odpowiedź co Alcon, z tym że nie sądzę, że jego wola zadziała, ponieważ ten wpis jest nadal zamawiany przez klucz - w takim przypadku nie można wywołać metody .remove() z wartością .

Zakłada się także, że wartość ma być unikalna lub że chcesz usunąć również wszystkie duplikaty z mapy.

6

chciałbym skorzystać z tej

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

edit: ponieważ obiekty są oznaczone przez "wskaźnik" nie przez wartość.

N

0

Nie sądzę, nastąpi to tylko raz w cyklu życia aplikacji.

Więc to, co bym zrobił, to delegować do innego obiektu odpowiedzialność za zachowanie odniesienia do obiektów dodanych do tej mapy.

Więc następnym razem trzeba go usunąć, należy użyć, że „reverse map” ...

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

Jest to o wiele szybciej niż iteracji.

Oczywiście należy je synchronizować. Ale nie powinno to być takie trudne, jeśli odtworzysz swój kod, aby jeden obiekt był odpowiedzialny za stan mapy.

Pamiętaj, że w OOP mamy obiekty, które mają stan i zachowanie.Jeśli dane są przesyłane wokół zmiennych w dowolnym miejscu, tworzysz niepotrzebne zależności między obiektami.

Tak, zajmie ci to trochę czasu, aby poprawić kod, ale czas spędzony na poprawieniu go, pozwoli Ci zaoszczędzić wiele bólu głowy w przyszłości. Pomyśl o tym.

+1

Jest to przypadek "ponownego wymyślania koła" lub w tym przypadku dwukierunkowej mapy. Kolekcje Google zapewniają dokładną implementację. –

+2

wymaga, aby wartość była unikatowa, co niekoniecznie jest konieczne. –

+0

@Steve: Cóż, oczywiście, należy dodać o wiele więcej kodu (np. Implementacja interfejsu mapy, aby móc synchronizować put i get). To bardziej przypomina "szkic" sugestii i nie jest "gotowy do kopiowania"/wklej "rozwiązanie. I tak, jako alternatywę mogą istnieć Kolekcje Google, ale ta sama zasada obowiązuje, powinien istnieć jeden obiekt odpowiedzialny za tę mapę i dwukierunkowe, a nie przekazujące odniesienia w całym kodzie. Dziękuję za komentarz. – OscarRyz

2

Podobnie jak większość innych plakatów, ogólnie rzecz biorąc, jest to operacja O (N), ponieważ będziesz musiał przeglądać całą listę wartości hashtable niezależnie. @tackline ma odpowiednie rozwiązanie dla utrzymania użycia pamięci w O (1) (dałem mu do tego prawo głosu).

Inną opcją jest poświęcenie miejsca w pamięci ze względu na szybkość. Jeśli twoja mapa ma rozsądne rozmiary, możesz przechowywać dwie mapy równolegle.

Jeśli masz mapę, zachowaj mapę równolegle do niej. Po wstawieniu/usunięciu na jednej mapie, zrób to także na drugiej. To prawda, że ​​jest to brzydsze, ponieważ marnujesz przestrzeń i musisz się upewnić, że metoda "hashCode" w DomainObj jest poprawnie zapisana, ale czas usuwania spadnie z O (N) do O (1), ponieważ możesz wyszukać klucz/mapowanie obiektów w stałym czasie w dowolnym kierunku.

Generalnie nie jest to najlepsze rozwiązanie, ale jeśli twoim problemem numer jeden jest szybkość, myślę, że jest to prawdopodobnie tak szybko, jak masz zamiar.

==================== Addendum: To w zasadzie to, co @msaeed zasugerowało po prostu sans biblioteki strony trzeciej.

2

Krótsze użycie iteratora polega na użyciu iteratora values ​​().

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

Oto rozwiązanie jedna linia:

map.values().remove(valueToRemove); 

To prawdopodobnie szybciej niż zdefiniowanie własnego iterator, ponieważ kod kolekcja JDK został znacznie zoptymalizowany.

Jak wspomnieli inni, bimapa będzie miała szybszą wartość, ale wymaga więcej pamięci i zajmuje więcej czasu. Ponadto, bimap działa tylko wtedy, gdy wartości są unikalne, co może, ale nie musi być w przypadku Twojego kodu.

59

Prawidłowy i szybki jednolinijkowy będzie w rzeczywistości: while (map.values().remove(valueObject));.

Coś dziwnego, że większość przykładów powyżej zakłada, że ​​obiekt wartości jest unikatowy.

+0

Który działa w podstawowych przypadkach użycia, a nie z predykatem pod warunkiem. – Rolf

Powiązane problemy