Płytka kopia mapy w języku Java


107

Jak rozumiem, istnieje kilka sposobów (być może także innych) na utworzenie płytkiej kopii pliku Mapw Javie:

Map<String, Object> data = new HashMap<String, Object>();
Map<String, Object> shallowCopy;

// first way
shallowCopy = new HashMap<String, Object>(data);

// second way
shallowCopy = (Map<String, Object>) ((HashMap<String, Object>) data).clone();

Czy jeden sposób jest preferowany od drugiego, a jeśli tak, to dlaczego?

Warto wspomnieć o tym, że druga metoda daje ostrzeżenie o „Unchecked Cast”. Musisz więc dodać, @SuppressWarnings("unchecked")aby to obejść, co jest trochę irytujące (patrz poniżej).

@SuppressWarnings("unchecked")
public Map<String, Object> getDataAsMap() {
    // return a shallow copy of the data map
    return (Map<String, Object>) ((HashMap<String, Object>) data).clone();
}

W nowszych wersjach Javy (dokładnie od Javy 10) można użyć statycznej metody Map.copyOf . Ale pamiętaj, że zwraca niemodyfikowalną mapę!
— Oleksandr Pyrohov

Odpowiedzi:


106

Zawsze lepiej jest kopiować za pomocą konstruktora kopiującego. clone()w Javie jest uszkodzony (patrz SO: Jak poprawnie zastąpić metodę clone? ).

Josh Bloch on Design - Copy Constructor versus Cloning

Jeśli przeczytałeś artykuł o klonowaniu w mojej książce, zwłaszcza jeśli czytasz między wierszami, będziesz wiedział, że moim zdaniem clonejest głęboko zepsuty. […] Szkoda, że Cloneablejest zepsuty, ale się zdarza.

Bloch (który, nawiasem mówiąc, zaprojektował i wdrożył framework Collection) poszedł nawet dalej, mówiąc, że dostarcza clone()metody tylko „dlatego, że ludzie tego oczekują”. W rzeczywistości wcale NIE zaleca jego używania.


Myślę, że bardziej interesującą debatą jest to, czy konstruktor kopii jest lepszy niż fabryka kopii, ale to zupełnie inna dyskusja.


1
Tak, to jedna z moich ulubionych części książki.
— smary wielogenowe

1
Nie lubię mówić, że clone () jest uszkodzony. Wolę powiedzieć, że klon był okropną decyzją projektową i może ci bardzo zaszkodzić, jeśli nie użyjesz go prawidłowo. Ponadto możesz nigdy nie ufać metodom clone () innych osób. Więc kończymy podobnie, staramy się tego unikać, ale nie jest zepsuty.
— santiagobasulto

4
Czy użycie edytora kopiowania nie wymaga, abyś wiedział, którą implementację mapy kopiujesz? Wydaje się, że jest to niepotrzebne ograniczenie.
— jon-hanson

"że udostępnia metodę clone () tylko" ponieważ ludzie tego oczekują "" - źródło?
— Adam Parkin,

60

Żaden z tych dwóch: konstruktor , do którego się odnosisz, nie jest zdefiniowany dla implementacji HashMap w Map (jak również dla innych), ale nie dla samego interfejsu Map (na przykład rozważ implementację dostawcy interfejsu Map: ty nie znajdzie tego konstruktora).

Z drugiej strony nie zaleca się stosowania tej clone()metody, jak wyjaśnia Josh Bloch.

W odniesieniu do interfejsu mapy (i twojego pytania, w którym pytasz, jak skopiować mapę, a nie HashMap), powinieneś użyć Map # putAll () :

Kopiuje wszystkie mapowania z określonej mapy do tej mapy (operacja opcjonalna). Efekt tego wywołania jest równoważny wywołaniu metody put (k, v) na tej mapie raz dla każdego odwzorowania klucza k na wartość v w określonej mapie.

Przykład:

// HashMap here, but it works for every implementation of the Map interface
Map<String, Object> data = new HashMap<String, Object>();
Map<String, Object> shallowCopy = new HashMap<String, Object>();

shallowCopy.putAll(data);

2
A więc wyjaśnienie: jeśli wiesz , że kopiujesz do implementacji, Mapktóra ma konstruktor kopiujący, nie ma powodu, aby nie używać konstruktora kopiującego?
— Adam Parkin

2
Dokładnie, możesz nawet pomyśleć na odwrót: jeśli używasz putAll, nie musisz wiedzieć, czy Mapimplementacja, której używasz, ma konstruktor kopiujący, czy nie. Tak więc zwykły konstruktor kopiujący dowolnej Mapimplementacji jest zbędny.
— Luca Fagioli

1
Jasne, chociaż generalnie wolę 1-wkładki niż 2-wkładki. ;)
— Adam Parkin

11

Skopiuj mapę bez znajomości jej implementacji:

static final Map shallowCopy(final Map source) throws Exception {
    final Map newMap = source.getClass().newInstance();
    newMap.putAll(source);
    return newMap;
}

3
Rozważ dodanie <K,V>parametrów typu, aby zapewnić bezpieczeństwo typu.
— Barett

1
A co z mapami bez konstruktorów bezargumentowych?
— Isaac Saffold
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.