Roteiro de implementação com chaves duplicadas
-
21-08-2019 - |
Pergunta
Eu quero ter um mapa com chaves duplicadas.
Eu sei que existem muitas implementações de mapa (Eclipse mostra-me cerca de 50), então eu aposto que deve haver um que permite isso. Eu sei que é fácil escrever seu próprio mapa que faz isso, mas eu prefiro usar alguma solução existente.
Talvez algo em commons-coleções ou o Google-coleções?
Solução
Você está à procura de um multimap, e na verdade ambos os commons-coleções e goiaba tem várias implementações para isso. Multimaps permitir várias chaves através da manutenção de um conjunto de valores por chave, ou seja, você pode colocar um único objeto no mapa, mas você recuperar uma coleção.
Se você pode usar Java 5, eu preferiria Guava Multimap
como é genéricos-aware.
Outras dicas
Nós não precisa depender dos Google Collections biblioteca externa. Você pode simplesmente implementar o seguinte mapa:
Map<String, ArrayList<String>> hashMap = new HashMap<String, ArrayList>();
public static void main(String... arg) {
// Add data with duplicate keys
addValues("A", "a1");
addValues("A", "a2");
addValues("B", "b");
// View data.
Iterator it = hashMap.keySet().iterator();
ArrayList tempList = null;
while (it.hasNext()) {
String key = it.next().toString();
tempList = hashMap.get(key);
if (tempList != null) {
for (String value: tempList) {
System.out.println("Key : "+key+ " , Value : "+value);
}
}
}
}
private void addValues(String key, String value) {
ArrayList tempList = null;
if (hashMap.containsKey(key)) {
tempList = hashMap.get(key);
if(tempList == null)
tempList = new ArrayList();
tempList.add(value);
} else {
tempList = new ArrayList();
tempList.add(value);
}
hashMap.put(key,tempList);
}
Por favor, certifique-se de ajustar o código.
Multimap<Integer, String> multimap = ArrayListMultimap.create();
multimap.put(1, "A");
multimap.put(1, "B");
multimap.put(1, "C");
multimap.put(1, "A");
multimap.put(2, "A");
multimap.put(2, "B");
multimap.put(2, "C");
multimap.put(3, "A");
System.out.println(multimap.get(1));
System.out.println(multimap.get(2));
System.out.println(multimap.get(3));
A saída é:
[A,B,C,A]
[A,B,C]
[A]
Nota: precisamos importar arquivos de biblioteca
.http://www.java2s.com/Code/Jar/g/ Downloadgooglecollectionsjar.htm
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
ou https://commons.apache.org/proper/commons-collections /download_collections.cgi
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;
Você poderia simplesmente passar uma matriz de valores para o valor em um HashMap regular, simulando assim chaves duplicadas, e seria até você para decidir quais os dados para uso.
Você também pode apenas usar um MultiMap , embora eu não gosto da idéia de chaves duplicadas mim mesmo.
Se você quiser iterar sobre uma lista de valor-chave em pares (como você escreveu no comentário), em seguida, uma lista ou um array deve ser melhor. Primeiro combinar as chaves e valores:
public class Pair
{
public Class1 key;
public Class2 value;
public Pair(Class1 key, Class2 value)
{
this.key = key;
this.value = value;
}
}
Substituir Class1 e Class2 com os tipos que você deseja usar para chaves e valores.
Agora você pode colocá-los em uma matriz ou uma lista e interagir sobre eles:
Pair[] pairs = new Pair[10];
...
for (Pair pair : pairs)
{
...
}
commons.apache.org
MultiValueMap class
Este problema pode ser resolvido com uma lista de List<Map.Entry<K,V>>
entrada do mapa. Nós não precisamos usar nem bibliotecas externas nem nova implementação do Mapa. A entrada do mapa pode ser criado assim:
Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);
aprender com meus erros ... por favor, não implementar isso em seu próprio país. Goiaba multimap é o caminho a percorrer.
A melhoria comum exigida em multimaps é impedir pares de chaves-valor duplicado.
Implementação / mudar isso de uma sua implementação pode ser irritante.
Em goiaba sua tão simples como:
HashMultimap<String, Integer> no_dupe_key_plus_val = HashMultimap.create();
ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();
Eu tive uma variante ligeiramente diferente desta questão: Foi obrigado a associar dois valores diferentes com mesma chave. Apenas publicá-la aqui, caso ele ajuda os outros, eu ter introduzido um HashMap como o valor:
/* @param frameTypeHash: Key -> Integer (frameID), Value -> HashMap (innerMap)
@param innerMap: Key -> String (extIP), Value -> String
If the key exists, retrieve the stored HashMap innerMap
and put the constructed key, value pair
*/
if (frameTypeHash.containsKey(frameID)){
//Key exists, add the key/value to innerHashMap
HashMap innerMap = (HashMap)frameTypeHash.get(frameID);
innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
} else {
HashMap<String, String> innerMap = new HashMap<String, String>();
innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
// This means the key doesn't exists, adding it for the first time
frameTypeHash.put(frameID, innerMap );
}
}
No código acima da frameID chave é lido da primeira corda de um arquivo de entrada em cada linha, o valor para frameTypeHash é construído pela divisão da linha remanescente e foi armazenado como objeto String originalmente, ao longo de um período de tempo o arquivo começou a ter várias linhas (com valores diferentes) associado com a mesma chave frameID, assim frameTypeHash foi substituído com última linha como o valor. Eu substituí o objeto String com outro objeto HashMap como o campo de valor, isso ajudou na manutenção de uma única tecla para mapeamento de valor diferente.
class DuplicateMap<K, V>
{
enum MapType
{
Hash,LinkedHash
}
int HashCode = 0;
Map<Key<K>,V> map = null;
DuplicateMap()
{
map = new HashMap<Key<K>,V>();
}
DuplicateMap( MapType maptype )
{
if ( maptype == MapType.Hash ) {
map = new HashMap<Key<K>,V>();
}
else if ( maptype == MapType.LinkedHash ) {
map = new LinkedHashMap<Key<K>,V>();
}
else
map = new HashMap<Key<K>,V>();
}
V put( K key, V value )
{
return map.put( new Key<K>( key , HashCode++ ), value );
}
void putAll( Map<K, V> map1 )
{
Map<Key<K>,V> map2 = new LinkedHashMap<Key<K>,V>();
for ( Entry<K, V> entry : map1.entrySet() ) {
map2.put( new Key<K>( entry.getKey() , HashCode++ ), entry.getValue());
}
map.putAll(map2);
}
Set<Entry<K, V>> entrySet()
{
Set<Entry<K, V>> entry = new LinkedHashSet<Map.Entry<K,V>>();
for ( final Entry<Key<K>, V> entry1 : map.entrySet() ) {
entry.add( new Entry<K, V>(){
private K Key = entry1.getKey().Key();
private V Value = entry1.getValue();
@Override
public K getKey() {
return Key;
}
@Override
public V getValue() {
return Value;
}
@Override
public V setValue(V value) {
return null;
}});
}
return entry;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{");
boolean FirstIteration = true;
for ( Entry<K, V> entry : entrySet() ) {
builder.append( ( (FirstIteration)? "" : "," ) + ((entry.getKey()==null) ? null :entry.getKey().toString() ) + "=" + ((entry.getValue()==null) ? null :entry.getValue().toString() ) );
FirstIteration = false;
}
builder.append("}");
return builder.toString();
}
class Key<K1>
{
K1 Key;
int HashCode;
public Key(K1 key, int hashCode) {
super();
Key = key;
HashCode = hashCode;
}
public K1 Key() {
return Key;
}
@Override
public String toString() {
return Key.toString() ;
}
@Override
public int hashCode() {
return HashCode;
}
}
Você poderia também explicar o contexto para o qual você está tentando implementar um mapa com chaves duplicadas? Estou certo de que poderia haver uma solução melhor. Mapas têm a intenção de manter as chaves únicas por uma boa razão. Embora se você realmente queria fazê-lo; você sempre pode estender a gravação classe uma classe mapa personalizado simples que tem uma função de atenuação de colisão e iria permitir-lhe manter múltiplas entradas com as mesmas chaves.
Nota: Você deve implementar a função de atenuação de colisão de tal forma que, as chaves de colisão são convertidos em conjunto único "sempre". Algo simples como, chave com código hash objeto ou algo anexando?
apenas para ser completo, Apache Commons Collections também tem um MultiMap . A desvantagem, claro, é que o Apache Commons não usa Genéricos.
Com um pouco hackear você pode usar HashSet com chaves duplicadas. AVISO: este é fortemente HashSet implementação dependentes.
class MultiKeyPair {
Object key;
Object value;
public MultiKeyPair(Object key, Object value) {
this.key = key;
this.value = value;
}
@Override
public int hashCode() {
return key.hashCode();
}
}
class MultiKeyList extends MultiKeyPair {
ArrayList<MultiKeyPair> list = new ArrayList<MultiKeyPair>();
public MultiKeyList(Object key) {
super(key, null);
}
@Override
public boolean equals(Object obj) {
list.add((MultiKeyPair) obj);
return false;
}
}
public static void main(String[] args) {
HashSet<MultiKeyPair> set = new HashSet<MultiKeyPair>();
set.add(new MultiKeyPair("A","a1"));
set.add(new MultiKeyPair("A","a2"));
set.add(new MultiKeyPair("B","b1"));
set.add(new MultiKeyPair("A","a3"));
MultiKeyList o = new MultiKeyList("A");
set.contains(o);
for (MultiKeyPair pair : o.list) {
System.out.println(pair.value);
}
}
Se houver chaves duplicadas, em seguida, uma chave pode corresponder a mais de um valor. A solução óbvia é para mapear a chave para uma lista desses valores.
Por exemplo, em Python:
map = dict()
map["driver"] = list()
map["driver"].append("john")
map["driver"].append("mike")
print map["driver"] # It shows john and mike
print map["driver"][0] # It shows john
print map["driver"][1] # It shows mike
Eu usei isso:
java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();
1, Map<String, List<String>> map = new HashMap<>();
esta solução detalhada tem várias desvantagens e é propenso a erros. isto implica que precisamos instanciar uma coleção para cada valor, verifique sua presença antes de adicionar ou remover um valor, excluí-lo manualmente não quando Os valores são deixados, etcetera.
2, org.apache.commons.collections4.MultiMap interface
3, com.google.common.collect.Multimap interface
que sobre tal impl MultiMap?
public class MultiMap<K, V> extends HashMap<K, Set<V>> {
private static final long serialVersionUID = 1L;
private Map<K, Set<V>> innerMap = new HashMap<>();
public Set<V> put(K key, V value) {
Set<V> valuesOld = this.innerMap.get(key);
HashSet<V> valuesNewTotal = new HashSet<>();
if (valuesOld != null) {
valuesNewTotal.addAll(valuesOld);
}
valuesNewTotal.add(value);
this.innerMap.put(key, valuesNewTotal);
return valuesOld;
}
public void putAll(K key, Set<V> values) {
for (V value : values) {
put(key, value);
}
}
@Override
public Set<V> put(K key, Set<V> value) {
Set<V> valuesOld = this.innerMap.get(key);
putAll(key, value);
return valuesOld;
}
@Override
public void putAll(Map<? extends K, ? extends Set<V>> mapOfValues) {
for (Map.Entry<? extends K, ? extends Set<V>> valueEntry : mapOfValues.entrySet()) {
K key = valueEntry.getKey();
Set<V> value = valueEntry.getValue();
putAll(key, value);
}
}
@Override
public Set<V> putIfAbsent(K key, Set<V> value) {
Set<V> valueOld = this.innerMap.get(key);
if (valueOld == null) {
putAll(key, value);
}
return valueOld;
}
@Override
public Set<V> get(Object key) {
return this.innerMap.get(key);
}
@Override
etc. etc. override all public methods size(), clear() .....
}