Je veux avoir une carte avec des clés en double.
Je sais qu'il existe de nombreuses implémentations de cartes (Eclipse m'en montre environ 50), alors je parie qu'il doit y en avoir une qui permet cela. Je sais qu'il est facile d'écrire votre propre carte, mais je préférerais utiliser une solution existante.
Peut-être quelque chose dans commons-collections ou google-collections?
Vous recherchez une carte multiple, et effectivement, les deux collections communes et Guava ont plusieurs implémentations pour cela. Les cartes multimédias permettent l’utilisation de plusieurs clés en conservant une collection de valeurs par clé, c’est-à-dire que vous pouvez insérer un seul objet dans la carte, mais vous récupérez une collection.
Si vous pouvez utiliser Java 5, je préférerais que Multimap
de Guava soit générique.
Nous n'avons pas besoin de dépendre de la bibliothèque externe Google Collections. Vous pouvez simplement implémenter la carte suivante:
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);
}
S'il vous plaît assurez-vous d'affiner le code.
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));
La sortie est:
[A,B,C,A]
[A,B,C]
[A]
Remarque: nous devons importer des fichiers de bibliothèque.
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;
Vous pouvez simplement transmettre un tableau de valeurs pour la valeur dans un HashMap standard, simulant ainsi des clés en double, et il vous appartient de décider des données à utiliser.
Vous pouvez aussi simplement utiliser un MultiMap , bien que je n'aime pas l'idée de dupliquer des clés moi-même.
Si vous voulez parcourir une liste de paires clé-valeur (comme vous l'avez écrit dans le commentaire), une liste ou un tableau devrait être préférable. Commencez par combiner vos clés et vos valeurs:
public class Pair
{
public Class1 key;
public Class2 value;
public Pair(Class1 key, Class2 value)
{
this.key = key;
this.value = value;
}
}
Remplacez Class1 et Class2 par les types que vous souhaitez utiliser pour les clés et les valeurs.
Maintenant, vous pouvez les mettre dans un tableau ou une liste et les parcourir:
Pair[] pairs = new Pair[10];
...
for (Pair pair : pairs)
{
...
}
commons.Apache.org
MultiValueMap class
Apprenez de mes erreurs ... s'il vous plait, n'appliquez pas ceci vous-même .
Une amélioration commune requise dans les cartes multimédias consiste à interdire les paires clé-valeur en double.
Implémenter/changer cela dans votre implémentation peut être ennuyeux.
En goyave, c'est aussi simple que:
HashMultimap<String, Integer> no_dupe_key_plus_val = HashMultimap.create();
ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();
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;
}
}
J'avais une variante légèrement différente de ce problème: il était nécessaire d'associer deux valeurs différentes avec la même clé. En le postant ici au cas où cela aiderait les autres, j'ai introduit un HashMap comme valeur:
/* @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 );
}
}
Dans le code ci-dessus, la clé frameID est lue à partir de la première chaîne d'un fichier d'entrée dans chaque ligne, la valeur de frameTypeHash est construite en scindant la ligne restante et a été stockée en tant qu'objet String à l'origine, sur une période donnée, le fichier a commencé à comporter plusieurs lignes ( avec des valeurs différentes) associées à la même clé frameID, donc frameTypeHash a été écrasé avec la dernière ligne comme valeur. J'ai remplacé l'objet String par un autre objet HashMap en tant que champ de valeur, ce qui a permis de maintenir le mappage d'une clé vers une autre valeur.
1, Map<String, List<String>> map = new HashMap<>();
cette solution verbeuse présente plusieurs inconvénients et est sujette aux erreurs. Il implique que nous devons instancier une Collection pour chaque valeur, vérifiez sa présence avant d’ajouter ou de supprimer une valeur, supprimez-la manuellement lorsque non. les valeurs sont laissées, etc.
2, org.Apache.commons.collections4.MultiMap interface
3, com.google.common.collect.Multimap interface
pour être complet, Apache Commons Collections possède également un MultiMap . L’inconvénient est qu’Apache Commons n’utilise pas de génériques.
qu'en est-il d'un tel 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() .....
}
Pourriez-vous également expliquer le contexte dans lequel vous essayez d'implémenter une carte avec des clés dupliquées? Je suis sûr qu'il pourrait y avoir une meilleure solution. Les cartes sont destinées à conserver des clés uniques pour une bonne raison. Bien que si vous vouliez vraiment le faire; vous pouvez toujours étendre la classe en écrivant une classe de carte personnalisée simple dotée d'une fonction d'atténuation des collisions et vous permettant de conserver plusieurs entrées avec les mêmes clés.
Remarque: vous devez implémenter une fonction d'atténuation des collisions de telle sorte que les clés en collision soient converties en un ensemble unique "toujours". Quelque chose de simple comme ajouter une clé avec un hashcode ou quelque chose?
S'il existe des clés en double, une clé peut correspondre à plusieurs valeurs. La solution évidente consiste à mapper la clé sur une liste de ces valeurs.
Par exemple en 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
Avec un peu de piratage, vous pouvez utiliser HashSet avec des clés en double. AVERTISSEMENT: cela dépend fortement de la mise en œuvre de HashSet.
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);
}
}