Implementazione della mappa con chiavi duplicate
-
21-08-2019 - |
Domanda
Voglio avere una mappa con chiavi duplicate.
So che ci sono molte implementazioni di mappe (Eclipse me ne mostra circa 50), quindi scommetto che ce ne deve essere una che lo consenta.So che è facile scrivere la tua mappa che faccia questo, ma preferirei usare qualche soluzione esistente.
Forse qualcosa in commons-collections o google-collections?
Soluzione
È alla ricerca di un multimap, e in effetti entrambi i commons-collezioni e Guava sono disponibili diverse implementazioni per questo. Multimaps consentono di chiavi multiple mantenendo un insieme di valori per chiave, vale a dire si può mettere un singolo oggetto nella mappa, ma recuperare una collezione.
Se è possibile utilizzare Java 5, preferirei di Guava Multimap
in quanto è in grado di riconoscere i generici.
Altri suggerimenti
Non abbiamo bisogno di dipendere da Google collezioni libreria esterna. Si può semplicemente implementare la seguente mappa:
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);
}
Assicurati di mettere a punto il codice.
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));
L'uscita è:
[A,B,C,A]
[A,B,C]
[A]
Nota: abbiamo bisogno di importare file di libreria
.http://www.java2s.com/Code/Jar/g/ Downloadgooglecollectionsjar.htm
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
https://commons.apache.org/proper/commons-collections /download_collections.cgi
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;
Si potrebbe semplicemente passare una matrice di valori per il valore in un HashMap regolare, simulando così chiavi duplicate, e sarebbe fino a voi decidere quali dati da utilizzare.
Si può anche semplicemente usare un MultiMap , anche se non mi piace l'idea di chiavi duplicate me stesso.
Se si desidera iterare su un elenco di valore di coppie di chiavi (come hai scritto nel commento), poi un elenco o una matrice dovrebbe essere migliore. Prima di combinare le chiavi e valori:
public class Pair
{
public Class1 key;
public Class2 value;
public Pair(Class1 key, Class2 value)
{
this.key = key;
this.value = value;
}
}
Sostituire Class1 e Class2 con i tipi che si desidera utilizzare per le chiavi e valori.
Ora si può mettere in un array o una lista e scorrere su di loro:
Pair[] pairs = new Pair[10];
...
for (Pair pair : pairs)
{
...
}
commons.apache.org
MultiValueMap class
Questo problema può essere risolto con una lista di carta ingresso List<Map.Entry<K,V>>
. Non abbiamo bisogno di utilizzare né librerie esterne né nuova implementazione di Map. Una voce di mappa può essere creato in questo modo:
Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);
imparare dai miei errori ... per favore non implementare da soli. Guava MultiMap è la strada da percorrere.
Un miglioramento comune required multimaps è non consentire coppie duplicate chiavi-valore.
L'implementazione / modifica questo in una propria implementazione può essere fastidioso.
Nella Guava sua semplice come:
HashMultimap<String, Integer> no_dupe_key_plus_val = HashMultimap.create();
ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();
Ho avuto un po 'diversa variante di questo problema: E' stato richiesto di associare due valori diversi con stessa chiave. Basta postare qui nel caso in cui aiuta gli altri, ho introdotto una HashMap come valore:
/* @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 );
}
}
Nel codice sopra il frameID chiave viene letta dalla prima stringa di un file di input in ogni riga, il valore per frameTypeHash è costruita suddividendo la linea rimanente e stato memorizzato come oggetto String inizialmente, per un periodo di tempo file cominciato avere più linee (con valori diversi) associati con la stessa chiave frameID, così frameTypeHash è sovrascritto con ultima riga come valore. Ho sostituito l'oggetto String con un altro oggetto HashMap come il campo del valore, questo ha aiutato a mantenere la sola chiave per la mappatura valore diverso.
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;
}
}
Potresti anche spiegare il contesto per il quale stai cercando di implementare una mappa con chiavi duplicate?Sono sicuro che potrebbe esserci una soluzione migliore.Le mappe hanno lo scopo di mantenere chiavi univoche per una buona ragione.Anche se davvero volessi farlo;puoi sempre estendere la classe scrivendo una semplice classe di mappa personalizzata che abbia una funzione di mitigazione delle collisioni e ti consenta di mantenere più voci con le stesse chiavi.
Nota:È necessario implementare la funzione di mitigazione delle collisioni in modo tale che le chiavi in collisione vengano convertite in un set univoco "sempre".Qualcosa di semplice come aggiungere la chiave con l'hashcode dell'oggetto o qualcosa del genere?
solo per essere completa, Apache Commons Collections ha anche un MultiMap . Il rovescio della medaglia, naturalmente, è che Apache Commons non fa uso di farmaci generici.
Con un po 'hack possibile utilizzare HashSet con chiavi duplicate. ATTENZIONE: questo è fortemente HashSet attuazione dipendente.
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 ci sono chiavi duplicate poi una chiave può corrispondere a più di un valore. La soluzione più ovvia è quella di mappare la chiave per un elenco di questi valori.
Ad esempio in 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
Ho usato questa:
java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();
1, Map<String, List<String>> map = new HashMap<>();
questa soluzione dettagliato ha molteplici inconvenienti ed è soggetto a errori. esso implica che abbiamo bisogno di istanziare una collezione per ogni valore, verificare la presenza di la sua presenza prima di aggiungere o rimuovere un valore, eliminarlo manualmente quando nessun I valori sono lasciati, eccetera.
2, org.apache.commons.collections4.MultiMap interface
3, com.google.common.collect.Multimap interface
che dire di un tale 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() .....
}