Cópia rasa de um mapa em Java

106

Pelo que entendi, existem algumas maneiras (talvez outras também) de criar uma cópia superficial de um Mapem Java:

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

Uma forma é preferida à outra e, em caso afirmativo, por quê?

Uma coisa que vale a pena mencionar é que a segunda forma dá um aviso de "Elenco não verificado". Então você tem que adicionar @SuppressWarnings("unchecked")para contornar isso, o que é um pouco irritante (veja abaixo).

@SuppressWarnings("unchecked")
public Map<String, Object> getDataAsMap() {
    // return a shallow copy of the data map
    return (Map<String, Object>) ((HashMap<String, Object>) data).clone();
}
dcp
fonte
Nas versões mais recentes do Java (desde o Java 10, para ser exato), você pode usar o método de fábrica estático Map.copyOf . Mas observe que ele retorna um mapa não modificável!
Oleksandr Pyrohov

Respostas:

106

É sempre melhor copiar usando um construtor de cópia. clone()em Java está quebrado (consulte SO: Como substituir corretamente o método de clone? ).

Josh Bloch no projeto - Construtor de cópia versus clonagem

Se você leu o item sobre clonagem em meu livro, especialmente se você leu nas entrelinhas, você saberá que acho que cloneestá profundamente quebrado. [...] é uma pena que Cloneableestá quebrado, mas acontece.

Bloch (que, a propósito, projetou e implementou o framework Collection) foi ainda mais longe ao dizer que ele só fornece o clone()método apenas "porque as pessoas o esperam". Na verdade, ele NÃO recomenda usá-lo.


Acho que o debate mais interessante é se um construtor de cópias é melhor do que uma fábrica de cópias, mas essa é uma discussão totalmente diferente.

poligenelubrificantes
fonte
1
Sim, esta é uma das minhas partes favoritas do livro.
poligenelubrificantes de
1
Não gosto de dizer que clone () está quebrado. Prefiro dizer que clone foi uma péssima decisão de design e pode prejudicá-lo muito se não for usado corretamente. Além disso, você pode nunca confiar nos métodos clone () de outras pessoas. Então a gente acaba parecido, tenta evitar, mas não está quebrado.
santiagobasulto
4
O uso do ctor de cópia não exige que você saiba qual implementação do Map você está copiando? Parece uma limitação desnecessária.
Jon-hanson de
"que ele fornece o método clone () apenas" porque as pessoas o esperam "" - fonte?
Adam Parkin
60

Nenhum dos dois: o construtor ao qual você está se referindo é definido para a implementação HashMap de um Mapa , (bem como para outros), mas não para a própria interface do Mapa (por exemplo, considere a implementação do Provedor da interface do Mapa: você não encontrará esse construtor).

Por outro lado, não é aconselhável usar o clone()método, conforme explica Josh Bloch.

Em relação à interface do Mapa (e de sua pergunta, na qual você pergunta como copiar um Mapa, não um HashMap), você deve usar Map # putAll () :

Copia todos os mapeamentos do mapa especificado para este mapa (operação opcional). O efeito dessa chamada é equivalente ao de chamar put (k, v) neste mapa uma vez para cada mapeamento da chave k ao valor v no mapa especificado.

Exemplo:

// 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);
Luca Fagioli
fonte
2
Então, para esclarecer: se você sabe que está copiando para uma implementação Mapque tem um construtor de cópia, não há razão para não usar o construtor de cópia então?
Adam Parkin
2
Exatamente, e você pode até pensar que o contrário: se você usar o putAllque você não precisa de saber se a Mapaplicação que você está usando tem um construtor de cópia ou não. Um construtor de simples cópia de qualquer Mapimplementação é, portanto, redundante.
Luca Fagioli
1
Claro, embora geralmente eu goste mais de 1 linha do que de 2 linhas. ;)
Adam Parkin
11

Copie um mapa sem saber sua implementação:

static final Map shallowCopy(final Map source) throws Exception {
    final Map newMap = source.getClass().newInstance();
    newMap.putAll(source);
    return newMap;
}
Terris
fonte
3
Considere adicionar <K,V>parâmetros de tipo para ajudar a garantir a segurança do tipo.
Barett
1
E os mapas sem construtores de argumento zero?
Isaac Saffold