Je recherche la solution la plus simple pour créer plusieurs index sur une collection Java.
Fonctionnalité requise:
Conditions latérales:
Bien sûr, je pourrais écrire un cours qui gère plusieurs cartes moi-même (ce n’est pas difficile, mais c’est comme réinventer la roue). J'aimerais donc savoir si cela peut être fait sans, tout en obtenant un usage simple, similaire à l'utilisation d'un seul fichier Java.util.Map indexé.
Merci chris
On dirait bien que nous n'avons rien trouvé. J'aime toutes vos réponses - les versions développées par vous-même, les liens vers des bibliothèques de type base de données.
Voici ce que je souhaite vraiment: disposer des fonctionnalités de (a) Apache Commons Collections ou (b) de Google Collections/Guava. Ou peut-être une très bonne alternative.
Est-ce que d'autres personnes manquent cette fonctionnalité dans ces bibliothèques? Ils fournissent toutes sortes de choses telles que MultiMaps, MulitKeyMaps, BidiMaps, ... Je pense que cela rentrerait bien dans ces bibliothèques - ça pourrait s'appeler MultiIndexMap
. Qu'est-ce que tu penses?
Chaque index sera fondamentalement une Map
séparée. Vous pouvez (et devriez probablement) résumer ceci derrière une classe qui gère les recherches, l’indexation, les mises à jour et les suppressions pour vous. Ce ne serait pas difficile de faire cela de façon assez générique. Mais non, il n’existe pas de classe standard dans cette classe, bien qu’elle puisse être facilement construite à partir des classes Java Collections.
Jetez un coup d’œil à CQEngine (moteur de recherche de collection) , c’est un choix parfait pour ce type de besoin, étant fondé sur une IndexedCollection
.
Voir également la question connexe Comment interrogez-vous les collections d'objets en Java (critères/type SQL)? pour plus de fond.
Ma première pensée serait de créer une classe pour la chose indexée, puis de créer plusieurs HashMap pour contenir les index, avec le même objet ajouté à chaque HashMaps. Pour un ajout, vous devez simplement ajouter le même objet à chaque HashMap. Une suppression nécessiterait de rechercher dans chaque HashMap la référence à l'objet de destination. Si les suppressions doivent être rapides, vous pouvez créer deux HashMaps pour chaque index: un pour index-valeur et l'autre pour valeur-index. Bien sûr, je mettrais tout ce que vous faites dans une classe avec une interface clairement définie.
Cela ne semble pas être difficile. Si vous connaissez les nombres et les types d'index et la classe du widget à l'avance, ce serait assez simple, par exemple:
public class MultiIndex
{
HashMap<String,Widget> index1=new HashMap<String,Widget>();
HashMap<String,Widget> index2=new HashMap<String,Widget>();
HashMap<Integer,Widget> index3=new HashMap<Integer,Widget>();
public void add(String index1Value, String index2Value, Integer index3Value, Widget widget)
{
index1.put(index1Value, widget);
index2.put(index2Value, widget);
index3.put(index3Value, widget);
}
public void delete(Widget widget)
{
Iterator i=index1.keySet().iterator();
while (i.hasNext())
{
String index1Value=(String)i.next();
Widget gotWidget=(Widget) index1.get(index1Value);
if (gotWidget.equals(widget))
i.remove();
}
... similarly for other indexes ...
}
public Widget getByIndex1(String index1Value)
{
return index1.get(index1Value);
}
... similarly for other indexes ...
}
}
Si vous voulez le rendre générique et accepter n'importe quel objet, avoir un nombre variable et des types d'index, etc., c'est un peu plus compliqué, mais pas beaucoup.
Vous avez beaucoup d'exigences très contraignantes qui semblent être très spécifiques à vos besoins. La plupart des choses que vous dites non viables sont dues au fait que beaucoup de gens ont exactement les mêmes besoins, ce qui définit fondamentalement un moteur de base de données. C'est pourquoi ce sont de "grandes" bibliothèques. Vous dites "pas de base de données" mais chaque système d'indexation est essentiellement une "base de données" de termes et de documents. Je dirais qu'une collection est une "base de données". Je dirais jetez un oeil à Space4J .
Je dirais que si vous ne trouvez pas ce que vous recherchez, démarrez un projet sur GitHub et collez-le vous-même et partagez les résultats.
Google CollectionsLinkedListMultimap
A propos de votre première exigence
Je pense qu'il n'y a ni une bibliothèque ni une aide qui la supporte.
Voici comment j'ai fait en utilisant LinkedListMultimap
Multimap<Integer, String> multimap = LinkedListMultimap.create();
// Three duplicates entries
multimap.put(1, "A");
multimap.put(2, "B");
multimap.put(1, "A");
multimap.put(4, "C");
multimap.put(1, "A");
System.out.println(multimap.size()); // outputs 5
Pour obtenir votre première exigence, un assistant peut jouer un bon travail
public static <K, V> void removeAllIndexEntriesAssociatedWith(Multimap<K, V> multimap, V value) {
Collection<Map.Entry<K, V>> eCollection = multimap.entries();
for (Map.Entry<K, V> entry : eCollection)
if(entry.getValue().equals(value))
eCollection.remove(entry);
}
...
removeAllIndexEntriesAssociatedWith(multimap, "A");
System.out.println(multimap.size()); // outputs 2
Google collections est
Vous devez vérifier Boon. :)
http://rick-hightower.blogspot.com/2013/11/what-if-Java-collections-and-Java.html
Vous pouvez ajouter un nombre n d'index de recherche et d'index de recherche. Il vous permet également d'interroger efficacement les propriétés primitives.
Voici un exemple tiré du wiki (je suis l'auteur).
repoBuilder.primaryKey("ssn")
.searchIndex("firstName").searchIndex("lastName")
.searchIndex("salary").searchIndex("empNum", true)
.usePropertyForAccess(true);
Vous pouvez remplacer cela en fournissant un indicateur true comme second argument de searchIndex.
Notez que empNum est un index unique interrogeable.
Et s'il était facile d'interroger un ensemble complexe d'objets Java au moment de l'exécution? Et si une API maintenait la synchronisation de vos index d’objets (en fait, il ne s’agissait que de TreeMaps et de HashMaps)? Eh bien, vous auriez le rapport de données de Boon. Cet article explique comment utiliser les utilitaires de dépôt de données de Boon pour interroger des objets Java. Ceci est la première partie. Il peut y avoir beaucoup, beaucoup de parties. :) Le référentiel de données de Boon facilite beaucoup les requêtes d'index sur les collections . Pourquoi le référentiel de données de Boon
Le référentiel de données de Boon vous permet de traiter les collections Java plus comme une base de données, du moins lorsqu'il s'agit d'interroger les collections. Le référentiel de données de Boon n'est pas une base de données en mémoire et ne peut pas remplacer l'organisation de vos objets dans des structures de données optimisées pour votre application. Si vous souhaitez passer votre temps à fournir de la valeur client, à construire vos objets et vos classes et à utiliser l'API de collections pour vos structures de données, DataRepo est fait pour vous. Cela n'empêche pas de sortir les livres de Knuth et de proposer une structure de données optimisée. Cela aide simplement à garder les choses banales faciles afin que vous puissiez passer votre temps à rendre les choses difficiles possibles . Nées du besoin
Ce projet est né d'un besoin. Je travaillais sur un projet qui prévoyait de stocker rapidement une grande collection d'objets de domaine en mémoire. Quelqu'un a posé une question très complexe que j'ai oubliée. Comment allons-nous interroger ces données. Ma réponse était que nous allons utiliser l'API Collections et l'API Streaming. Ensuite, j'ai essayé de faire cela ... Hmmm ... Je me suis aussi fatigué d'utiliser l'API de flux JDK 8 sur un grand ensemble de données, et c'était lent. (Le dépôt de données de Boon fonctionne avec JDK7 et JDK8). C'était une recherche/filtre linéaire. C'est par conception, mais pour ce que je faisais, cela n'a pas fonctionné. Il me fallait des index pour prendre en charge des requêtes arbitraires .. Le référentiel de données de Boon augmente l'API de diffusion en continu.
Le référentiel de données de Boon ne tente pas de remplacer l'API de flux JDK 8 et, en fait, il fonctionne bien. Le référentiel de données de Boon vous permet de créer des collections indexées. Les index peuvent être n'importe quoi (il est enfichable) . À l'heure actuelle, les index de référentiels de données de Boon sont basés sur ConcurrentHashMap et ConcurrentSkipListMap . De par leur conception, le référentiel de données de Boon fonctionne avec des bibliothèques de collections standard. Il n'est pas prévu de créer un ensemble de collections personnalisées. On devrait pouvoir brancher Goyave, Arbres Concurrents ou Trove si on le souhaite .. Il fournit une API simplifiée pour le faire. Il permet la recherche linéaire pour un sentiment d'achèvement, mais je recommande de l'utiliser principalement pour utiliser des index, puis d'utiliser l'API de transmission en continu pour le reste (pour le type sécurité et la vitesse).
aperçu avant l'étape par étape
Disons que vous avez une méthode qui crée 200 000 objets employés comme ceci:
List<Employee> employees = TestHelper.createMetricTonOfEmployees(200_000);
Nous avons donc maintenant 200 000 employés. Fouillons-les ...
Commencez par insérer les employés dans une requête interrogeable:
employees = query(employees);
Maintenant chercher:
List<Employee> results = query(employees, eq("firstName", firstName));
Alors, quelle est la principale différence entre ce qui précède et l’API de flux?
employees.stream().filter(emp -> emp.getFirstName().equals(firstName)
Un facteur environ 20 000% plus rapide pour utiliser le DataRepo de Boon! Ah la puissance de HashMaps et TreeMaps. :) Il existe une API qui ressemble à vos collections intégrées. Il existe également une API qui ressemble davantage à un objet DAO ou à un objet Repo.
Une requête simple avec l'objet Repo/DAO ressemble à ceci:
List<Employee> employees = repo.query(eq("firstName", "Diana"));
Une requête plus impliquée ressemblerait à ceci:
List<Employee> employees = repo.query(
and(eq("firstName", "Diana"), eq("lastName", "Smith"), eq("ssn", "21785999")));
Ou ca:
List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), lte("salary", 200_000),
gte("salary", 190_000)));
Ou même ceci:
List<Employee> employees = repo.query(
and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), between("salary", 190_000, 200_000)));
Ou si vous souhaitez utiliser l'API de flux JDK 8, cela ne fonctionne pas avec:
int sum = repo.query(eq("lastName", "Smith")).stream().filter(emp -> emp.getSalary()>50_000)
.mapToInt(b -> b.getSalary())
.sum();
Ce qui précède serait beaucoup plus rapide si le nombre d'employés était assez important. Cela limiterait les employés dont le nom commençait par Smith et touchait un salaire supérieur à 50 000. Supposons que vous aviez 100 000 employés et seulement 50 nommés Smith. Maintenant, vous réduisez rapidement à 50 en utilisant l’indice qui tire effectivement 50 employés de 100 000, puis nous ne filtrons que 50 personnes au lieu de 100 000.
Voici un point de repère exécuté à partir du dépôt de données d'une recherche linéaire par rapport à une recherche indexée en nanosecondes:
Name index Time 218
Name linear Time 3709120
Name index Time 213
Name linear Time 3606171
Name index Time 219
Name linear Time 3528839
Quelqu'un m'a récemment dit: "Mais avec l'API de streaming, vous pouvez utiliser le filtre en parallèle).
Voyons comment le calcul tient le coup:
3,528,839 / 16 threads vs. 219
201,802 vs. 219 (nano-seconds).
Les index gagnent, mais c'était une photo finish. NE PAS! :)
Il était seulement 9 500% plus rapide au lieu de 40 000% plus rapide. Si proche .....J'ai ajouté quelques fonctionnalités supplémentaires. Ils font un usage intensif des index. :).
Repo.updateByFilter (valeurs (valeur ("prénom", "Di")), et (eq ("prénom", "Diana"), eq ("prénom", "Smith"), eq ("ssn", "21785999")));
ce qui précède équivaudrait à
UPDATE Employé e SET e.firstName = 'Di' WHERE e.firstName = 'Diana' et e.lastName = 'Smith' et e.ssn = '21785999'
Cela vous permet de définir plusieurs champs à la fois sur plusieurs enregistrements, donc si vous effectuez une mise à jour en bloc.
Il existe des méthodes surchargées pour tous les types de base, donc si vous avez une valeur à mettre à jour pour chaque élément renvoyé par un filtre:.
repo.updateByFilter("firstName", "Di",
and( eq("firstName", "Diana"),
eq("lastName", "Smith"),
eq("ssn", "21785999") ) );
List <Map<String, Object>> list = repo.query(selects(select("firstName")), eq("lastName", "Hightower"));
List <Map<String, Object>> list = repo.sortedQuery("firstName",selects(select("firstName")), eq("lastName", "Hightower"));
List <Map<String, Object>> list = repo.query( selects(select("department", "name")), eq("lastName", "Hightower")); assertEquals("engineering", list.get(0).get("department.name"));
.List <Map<String, Object>> list = repo.query( selects(selectPropPath("department", "name")), eq("lastName", "Hightower"));
.repoBuilder.primaryKey("ssn") .searchIndex("firstName").searchIndex("lastName") .searchIndex("salary").searchIndex("empNum", true) .usePropertyForAccess(true);
.List<Map<String, Object>> employees = repo.queryAsMaps(eq("firstName", "Diana"));
System.out.println(employees.get(0).get("department"));
{class=Department, name=engineering}
List <Map<String, Object>> list = repo.query( selects(select("tags", "metas", "metas2", "metas3", "name3")), eq("lastName", "Hightower")); print("list", list); assertEquals("3tag1", idx(list.get(0).get("tags.metas.metas2.metas3.name3"), 0));
list [{tags.metas.metas2.metas3.name3=[3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3, 3tag1, 3tag2, 3tag3]}, ...
public class Employee { List <Tag> tags = new ArrayList<>(); { tags.add(new Tag("tag1")); tags.add(new Tag("tag2")); tags.add(new Tag("tag3")); } ... public class Tag { ... List<Meta> metas = new ArrayList<>(); { metas.add(new Meta("mtag1")); metas.add(new Meta("mtag2")); metas.add(new Meta("mtag3")); } } public class Meta { ... List<Meta2> metas2 = new ArrayList<>(); { metas2.add(new Meta2("2tag1")); metas2.add(new Meta2("2tag2")); metas2.add(new Meta2("2tag3")); } } ... public class Meta2 { List<Meta3> metas3 = new ArrayList<>(); { metas3.add(new Meta3("3tag1")); metas3.add(new Meta3("3tag2")); metas3.add(new Meta3("3tag3")); } public class Meta3 { ...
List<Employee> results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee")); assertEquals(1, results.size()); assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
List<Employee> results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee")); assertEquals(1, results.size()); assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
List<Employee> results = sortedQuery(queryableList, "firstName", instanceOf(SalesEmployee.class)); assertEquals(1, results.size()); assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
List<Employee> results = sortedQuery(queryableList, "firstName", implementsInterface(Comparable.class)); assertEquals(1, results.size()); assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName());
/* Create a repo, and decide what to index. */ RepoBuilder repoBuilder = RepoBuilder.getInstance(); /* Look at the nestedIndex. */ repoBuilder.primaryKey("id") .searchIndex("firstName").searchIndex("lastName") .searchIndex("salary").uniqueSearchIndex("empNum") .nestedIndex("tags", "metas", "metas2", "name2");
List<Map<String, Object>> list = repo.query( selects(select("tags", "metas", "metas2", "name2")), eqNested("2tag1", "tags", "metas", "metas2", "name2"));
.List<Map<String, Object>> list = repo.query( selects(select("tags", "metas", "metas2", "name2")), eq("tags.metas.metas2.name2", "2tag1"));
List<Employee> queryableList = $q(h_list, Employee.class, SalesEmployee.class, HourlyEmployee.class); List<Employee> results = sortedQuery(queryableList, "firstName", eq("commissionRate", 1)); assertEquals(1, results.size()); assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName()); results = sortedQuery(queryableList, "firstName", eq("weeklyHours", 40)); assertEquals(1, results.size()); assertEquals("HourlyEmployee", results.get(0).getClass().getSimpleName());
The data repo has a similar feature in its DataRepoBuilder.build(...) method for specifying subclasses. This allows you to seemless query fields form subclasses and classes in the same repo or searchable collection.
J'ai écrit une interface de table qui inclut des méthodes telles que
V put(R rowKey, C columnKey, V value)
V get(Object rowKey, Object columnKey)
Map<R,V> column(C columnKey)
Set<C> columnKeySet()
Map<C,V> row(R rowKey)
Set<R> rowKeySet()
Set<Table.Cell<R,C,V>> cellSet()
Nous aimerions l'inclure dans une future version de Guava, mais je ne sais pas quand cela se produira . http://code.google.com/p/guava-libraries/issues/detail?id = 173
Votre objectif principal semble être de supprimer l'objet de tous les index lorsque vous le supprimez.
La méthode la plus simple consiste à ajouter une autre couche d'indirection: vous stockez votre objet dans un Map<Long,Value>
et utilisez une carte bidirectionnelle (que vous trouverez dans Jakarta Commons et probablement Google Code) pour vos index sous la forme Map<Key,Long>
. Lorsque vous supprimez une entrée d'un index particulier, vous prenez la valeur Long
de cet index et vous l'utilisez pour supprimer les entrées correspondantes de la carte principale et des autres index.
Une alternative à BIDIMap consiste à définir vos cartes "index" comme suit: Map<Key,WeakReference<Long>>
; toutefois, cela nécessitera de mettre en œuvre une ReferenceQueue
pour le nettoyage.
Une autre alternative consiste à créer un objet clé pouvant prendre un Tuple arbitraire, définir sa méthode equals()
pour apparier n'importe quel élément du Tuple et l'utiliser avec une TreeMap
. Vous ne pouvez pas utiliser HashMap
, car vous ne pourrez pas calculer un hashcode basé sur un seul élément du tuple.
public class MultiKey
implements Comparable<Object>
{
private Comparable<?>[] _keys;
private Comparable _matchKey;
private int _matchPosition;
/**
* This constructor is for inserting values into the map.
*/
public MultiKey(Comparable<?>... keys)
{
// yes, this is making the object dependent on externally-changable
// data; if you're paranoid, copy the array
_keys = keys;
}
/**
* This constructor is for map probes.
*/
public MultiKey(Comparable key, int position)
{
_matchKey = key;
_matchPosition = position;
}
@Override
public boolean equals(Object obj)
{
// verify that obj != null and is castable to MultiKey
if (_keys != null)
{
// check every element
}
else
{
// check single element
}
}
public int compareTo(Object o)
{
// follow same pattern as equals()
}
}
Utilisez PrefuseTables . Ils supportent autant d'indices que vous le souhaitez, sont rapides (les index sont des TreeMaps) et ont des options de filtrage Nice (filtres booléens? Aucun problème!). Aucune base de données requise, testée avec de grands ensembles de données dans de nombreuses applications de visualisation d'informations.
Sous leur forme brute, ils ne sont pas aussi pratiques que les conteneurs standard (vous devez gérer des lignes et des colonnes), mais vous pouvez sûrement écrire une petite enveloppe autour de cela. De plus, ils se connectent parfaitement aux composants de l'interface utilisateur tels que les JTables de Swing.
Si vous souhaitez plusieurs index sur vos données, vous pouvez créer et gérer plusieurs mappages de hachage ou utiliser une bibliothèque telle que Data Store:
https://github.com/jparams/data-store
Exemple:
Store<Person> store = new MemoryStore<>() ;
store.add(new Person(1, "Ed", 3));
store.add(new Person(2, "Fred", 7));
store.add(new Person(3, "Freda", 5));
store.index("name", Person::getName);
Person person = store.getFirst("name", "Ed");
Avec Data Store, vous pouvez créer des index insensibles à la casse et toutes sortes de choses intéressantes. Cela vaut la peine de vérifier.
Je ne suis pas sûr de comprendre la question, mais je pense que ce que vous demandez, ce sont plusieurs façons de mapper des clés uniques et différentes en valeurs et en un nettoyage approprié lorsqu'une valeur disparaît.
Je vois que vous ne voulez pas lancer le vôtre, mais il existe une composition assez simple de carte et de multi-carte (j'ai utilisé la multi-carte Guava ci-dessous, mais celle d'Apache devrait également fonctionner) pour faire ce que vous voulez. J'ai une solution rapide et sale ci-dessous (ignoré les constructeurs, car cela dépend du type de carte/carte sous-jacente que vous souhaitez utiliser):
package edu.cap10.common.collect;
import Java.util.Collection;
import Java.util.Map;
import com.google.common.collect.ForwardingMap;
import com.google.common.collect.Multimap;
public class MIndexLookupMap<T> extends ForwardingMap<Object,T>{
Map<Object,T> delegate;
Multimap<T,Object> reverse;
@Override protected Map<Object, T> delegate() { return delegate; }
@Override public void clear() {
delegate.clear();
reverse.clear();
}
@Override public boolean containsValue(Object value) { return reverse.containsKey(value); }
@Override public T put(Object key, T value) {
if (containsKey(key) && !get(key).equals(value)) reverse.remove(get(key), key);
reverse.put(value, key);
return delegate.put(key, value);
}
@Override public void putAll(Map<? extends Object, ? extends T> m) {
for (Entry<? extends Object,? extends T> e : m.entrySet()) put(e.getKey(),e.getValue());
}
public T remove(Object key) {
T result = delegate.remove(key);
reverse.remove(result, key);
return result;
}
public void removeValue(T value) {
for (Object key : reverse.removeAll(value)) delegate.remove(key);
}
public Collection<T> values() {
return reverse.keySet();
}
}
la suppression est O (nombre de clés), mais tout le reste est dans le même ordre qu'une implémentation de carte typique (une mise à l'échelle constante supplémentaire, car vous devez également ajouter des éléments à l'inverse).
Je viens d'utiliser des clés Object
(cela devrait convenir avec les implémentations appropriées de equals()
et hashCode()
et distinction des clés) - mais vous pouvez également avoir un type de clé plus spécifique.
En principe, une solution basée sur plusieurs cartes de hachage serait possible, mais dans ce cas, elles doivent toutes être mises à jour manuellement. Une solution intégrée très simple peut être trouvée ici: http://insidecoffe.blogspot.de/2013/04/indexable-hashmap-implementation.html
regardons le projet http://code.google.com/p/multiindexcontainer/wiki/MainPage C’est une façon généralisée d’utiliser des cartes pour les getters JavaBean et d’effectuer des recherches sur les valeurs indexées ..__ pense que c'est ce que vous recherchez. Essayons.
Voici comment je parviens à cela. À l’heure actuelle, seuls les méthodes de travail, de suppression et d’obtention fonctionnent pour vous reposer, vous devez remplacer les méthodes souhaitées.
Exemple:
MultiKeyMap<MultiKeyMap.Key,String> map = new MultiKeyMap<>();
MultiKeyMap.Key key1 = map.generatePrimaryKey("keyA","keyB","keyC");
MultiKeyMap.Key key2 = map.generatePrimaryKey("keyD","keyE","keyF");
map.put(key1,"This is value 1");
map.put(key2,"This is value 2");
Log.i("MultiKeyMapDebug",map.get("keyA"));
Log.i("MultiKeyMapDebug",map.get("keyB"));
Log.i("MultiKeyMapDebug",map.get("keyC"));
Log.i("MultiKeyMapDebug",""+map.get("keyD"));
Log.i("MultiKeyMapDebug",""+map.get("keyE"));
Log.i("MultiKeyMapDebug",""+map.get("keyF"));
Sortie:
MultiKeyMapDebug: This is value 1
MultiKeyMapDebug: This is value 1
MultiKeyMapDebug: This is value 1
MultiKeyMapDebug: This is value 2
MultiKeyMapDebug: This is value 2
MultiKeyMapDebug: This is value 2
MultiKeyMap.Java:
/**
* Created by hsn on 11/04/17.
*/
public class MultiKeyMap<K extends MultiKeyMap.Key, V> extends HashMap<MultiKeyMap.Key, V> {
private Map<String, MultiKeyMap.Key> keyMap = new HashMap<>();
@Override
public V get(Object key) {
return super.get(keyMap.get(key));
}
@Override
public V put(MultiKeyMap.Key key, V value) {
List<String> keyArray = (List<String>) key;
for (String keyS : keyArray) {
keyMap.put(keyS, key);
}
return super.put(key, value);
}
@Override
public V remove(Object key) {
return super.remove(keyMap.get(key));
}
public Key generatePrimaryKey(String... keys) {
Key singleKey = new Key();
for (String key : keys) {
singleKey.add(key);
}
return singleKey;
}
public class Key extends ArrayList<String> {
}
}