Index multiples pour une collection Java – la solution la plus élémentaire?

Je recherche la solution la plus simple pour créer plusieurs index sur une collection Java.

Fonctionnalité requirejse:

  • Lorsqu’une valeur est supprimée, toutes les entrées d’index associées à cette valeur doivent être supprimées.
  • La recherche d’index doit être plus rapide que la recherche linéaire (au moins aussi rapide qu’un TreeMap).

Conditions secondaires:

  • Pas de dépendances sur les grandes bibliothèques (comme Lucene). Aucune bibliothèque rare ou peu testée. Pas de firebase database.
  • Une bibliothèque comme Apache Commons Collections, etc. serait acceptable.
  • Encore mieux, si cela fonctionne uniquement avec JavaSE (6.0).
  • Edit: Pas de solution auto-implémentée (merci pour les réponses suggérant ceci – il est bon de les avoir ici pour être complet, mais j’ai déjà une solution très similaire à celle de Jay) Chaque fois que plusieurs personnes découvrent faire partie d’une bibliothèque commune.

Bien sûr, je pourrais écrire une classe qui gère plusieurs cartes moi-même (ce n’est pas difficile, mais on a l’impression de réinventer la roue) . Donc, je voudrais savoir si cela peut être fait sans – tout en obtenant une utilisation simple, similaire à l’utilisation d’un seul fichier java.util.Map indexé.

Merci Chris

Mettre à jour

Cela ressemble beaucoup à ce que nous n’avons rien trouvé. J’aime toutes vos réponses – les versions auto-développées, les liens vers des bibliothèques de type firebase database.

Voici ce que je veux vraiment: avoir la fonctionnalité dans (a) Apache Commons Collections ou (b) dans Google Collections / Guava. Ou peut-être une très bonne alternative.

Est-ce que d’autres personnes manquent aussi cette fonctionnalité dans ces bibliothèques? Ils fournissent toutes sortes de choses comme MultiMaps, MulitKeyMaps, BidiMaps, … Je pense que cela pourrait bien s’intégrer dans ces bibliothèques – cela pourrait s’appeler MultiIndexMap . Qu’est-ce que tu penses?

Chaque index sera essentiellement une Map séparée. Vous pouvez (et probablement devriez) faire abstraction de cette tâche derrière une classe qui gère les recherches, l’indexation, les mises à jour et les retraits pour vous. Ce ne serait pas difficile de le faire de manière assez générique. Mais non, il n’y a pas de standard dans la classe pour cela bien qu’il puisse facilement être construit à partir des classes Java Collections.

Jetez un coup d’oeil à CQEngine (Collection Query Engine) , il convient parfaitement à ce type d’exigence, car il est basé sur une IndexedCollection .

Voir aussi la question connexe Comment interrogez -vous les collections d’objects 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 object ajouté à chacune des HashMaps. Pour un ajout, vous appendiez simplement le même object à chaque HashMap. Une suppression nécessiterait une recherche dans chaque HashMap pour la référence à l’object de destination. Si les suppressions doivent être rapides, vous souhaiterez peut-être créer deux HashMaps pour chaque index: l’une pour l’indice à la valeur et l’autre pour la valeur à l’index. Bien sûr, j’emballerais ce que vous faites dans une classe avec une interface clairement définie.

Cela ne semble pas difficile. Si vous connaissez le nombre et le type des index et la classe du widget, ce serait assez facile, comme par exemple:

 public class MultiIndex { HashMap index1=new HashMap(); HashMap index2=new HashMap(); HashMap index3=new HashMap(); public void add(Ssortingng index1Value, Ssortingng 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()) { Ssortingng index1Value=(Ssortingng)i.next(); Widget gotWidget=(Widget) index1.get(index1Value); if (gotWidget.equals(widget)) i.remove(); } ... similarly for other indexes ... } public Widget getByIndex1(Ssortingng index1Value) { return index1.get(index1Value); } ... similarly for other indexes ... } } 

Si vous voulez le rendre générique et accepter n’importe quel object, avoir un nombre et des types d’index variables, etc., c’est un peu plus compliqué, mais pas beaucoup.

Vous avez beaucoup d’exigences constantes qui semblent être très spécifiques à vos besoins. La plupart des choses que vous dites ne sont pas viables, car beaucoup de personnes ont exactement les mêmes besoins, ce qui définit essentiellement un moteur de firebase database de base. C’est pourquoi ce sont des “grandes” bibliothèques. Vous dites “pas de firebase database” mais chaque système d’indexation est essentiellement une “firebase database” de termes et de documents. Je dirais qu’une collection est une “firebase database”. Je dirais jeter un oeil à Space4J .

Je dirais que si vous ne trouvez pas ce que vous cherchez, lancez un projet sur GitHub et continuez à le coder vous-même et à partager les résultats.

Google Collections LinkedListMultimap

À propos de votre première exigence

  • Lorsqu’une valeur est supprimée, toutes les entrées d’index associées à cette valeur doivent être supprimées.

Je pense qu’il n’y a ni bibliothèque ni assistant qui le supporte.

Voici comment je l’ai fait en utilisant LinkedListMultimap

 Multimap multimap = LinkedListMultimap.create(); // Three duplicates ensortinges 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 faire du bon travail

 public static  void removeAllIndexEnsortingesAssociatedWith(Multimap multimap, V value) { Collection> eCollection = multimap.ensortinges(); for (Map.Entry entry : eCollection) if(entry.getValue().equals(value)) eCollection.remove(entry); } 

 removeAllIndexEnsortingesAssociatedWith(multimap, "A"); System.out.println(multimap.size()); // outputs 2 

Les collections Google sont

  • poids léger
  • Soutenu par Joshua Block (Effective Java)
  • Belles fonctionnalités comme ImmutableList, ImmutableMap, etc.

Vous devez vérifier Boon. 🙂

http://rick-hightower.blogspot.com/2013/11/what-if-java-collections-and-java.html

Vous pouvez append 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 vrai comme second argument à searchIndex.

Notez que empNum est un index unique consultable.

Et s’il était facile d’interroger un ensemble complexe d’objects Java lors de l’exécution? Et s’il y avait une API qui synchronisait vos index d’object (en fait, seulement TreeMaps et HashMaps)? Eh bien, vous auriez un repository de données de Boon. Cet article montre comment utiliser les utilitaires de repository de données de Boon pour interroger des objects Java. C’est la première partie. Il peut y avoir beaucoup de parties. 🙂 Le repository de données de Boon facilite grandement la réalisation de requêtes basées sur des index sur des collections. Pourquoi le repository de données Boon

Le repository de données de Boon vous permet de traiter les collections Java plus comme une firebase database, du moins lorsqu’il s’agit d’interroger les collections. Le référentiel de données de Boon n’est pas une firebase database en mémoire et ne peut pas remplacer l’organisation de vos objects en structures de données optimisées pour votre application. Si vous souhaitez consacrer tout votre temps à la création de valeur pour vos clients, à la création d’objects et de classes et à l’utilisation de l’API 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 pour que vous puissiez passer votre temps à rendre les choses difficiles. Né du besoin

Ce projet est né d’un besoin. Je travaillais sur un projet qui prévoyait de stocker en mémoire de grandes quantités d’objects de domaine en mémoire, et quelqu’un a posé une question importante que j’ai ignorée. Comment allons-nous interroger ces données? Ma réponse a été que nous utiliserons l’API Collections et l’API Streaming. Ensuite, j’ai essayé de le faire … Hmmm … J’étais aussi fatigué d’utiliser l’API de stream JDK 8 sur un grand dataset, et c’était lent. (Le repository 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 ne fonctionnait pas. J’avais besoin d’index pour prendre en charge des requêtes arbitraires. Le repository de données de Boon augmente l’API de diffusion.

Le repository de données de Boon ne cherche pas à remplacer l’API de stream JDK 8 et, en fait, il fonctionne bien avec lui. Le repository de données de Boon vous permet de créer des collections indexées. Les index peuvent être n’importe quoi (c’est pluggable). À l’heure actuelle, les index de repository de données de Boon sont basés sur ConcurrentHashMap et ConcurrentSkipListMap. De par sa conception, le repository de données de Boon fonctionne avec les bibliothèques de collection standard. Il n’est pas prévu de créer un ensemble de collections personnalisées. On devrait pouvoir twigr Guava, Arbres simultanés ou Trove si l’on veut le faire. Il fournit une API simplifiée pour ce faire. Il permet une recherche linéaire du sens d’achèvement, mais je recommande de l’utiliser principalement pour utiliser des index, puis utiliser l’API de diffusion pour le rest (pour le type de sécurité et de vitesse).

pic avant le pas à pas

Disons que vous avez une méthode qui crée 200 000 objects employés comme ceci:

  List employees = TestHelper.createMesortingcTonOfEmployees(200_000); 

Nous avons maintenant 200 000 employés. Cherchons les …

Envelopper d’abord les employés dans une requête consultable:

  employees = query(employees); 

Recherche maintenant:

  List results = query(employees, eq("firstName", firstName)); 

Quelle est la différence principale entre l’API ci-dessus et l’API de stream?

  employees.stream().filter(emp -> emp.getFirstName().equals(firstName) 

Environ 20 000% plus rapide d’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 object DAO ou à un object Repo.

Une simple requête avec l’object Repo / DAO ressemble à ceci:

  List employees = repo.query(eq("firstName", "Diana")); 

Une requête plus complexe ressemblerait à ceci:

  List employees = repo.query( and(eq("firstName", "Diana"), eq("lastName", "Smith"), eq("ssn", "21785999"))); 

Ou ca:

  List employees = repo.query( and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), lte("salary", 200_000), gte("salary", 190_000))); 

Ou même ceci:

  List employees = repo.query( and(startsWith("firstName", "Bob"), eq("lastName", "Smith"), between("salary", 190_000, 200_000))); 

Ou si vous voulez utiliser l’API de stream JDK 8, cela ne marche pas avec elle:

  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 élevé. Cela réduirait les employés dont le nom commençait par Smith et dont le salaire était supérieur à 50 000. Disons que vous aviez 100 000 employés et seulement 50 nommés Smith, alors maintenant vous réduisez à 50 rapidement en utilisant l’index qui attire effectivement 50 employés sur 100 000, puis nous faisons le filtre sur seulement 50 au lieu de 100 000.

Voici un benchmark tiré du repo de données d’une recherche linéaire par rapport à une recherche indexée en nano secondes:

 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 dit récemment: “Mais avec l’API de streaming, vous pouvez lancer le filtre dans parralel).

Voyons comment les maths tiennent le coup:

 3,528,839 / 16 threads vs. 219 201,802 vs. 219 (nano-seconds). 

Les index gagnent, mais c’était une photo finale. NE PAS! 🙂

Il était seulement 9 500% plus rapide au lieu de 40 000% plus rapide. Tellement proche …..

J’ai ajouté quelques fonctionnalités supplémentaires. Ils utilisent beaucoup d’index. 🙂

repo.updateByFilter (valeurs (value (“firstName”, “Di”)), et (eq (“firstName”, “Diana”), eq (“lastName”, “Smith”), eq (“ssn”, “21785999 “)));

Ce qui précède serait équivalent à

UPDATE Employee 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 groupée.

Il existe des méthodes surchargées pour tous les types de base, donc si vous avez une valeur à mettre à jour sur chaque élément renvoyé par un filtre:

  repo.updateByFilter("firstName", "Di", and( eq("firstName", "Diana"), eq("lastName", "Smith"), eq("ssn", "21785999") ) ); 

Voici quelques fonctionnalités de sélection de base:

  List > list = repo.query(selects(select("firstName")), eq("lastName", "Hightower")); 

Vous pouvez avoir autant de sélections que vous le souhaitez. Vous pouvez également ramener la liste sortingée:

  List > list = repo.sortedQuery("firstName",selects(select("firstName")), eq("lastName", "Hightower")); 

Vous pouvez sélectionner les propriétés des propriétés associées (par exemple, employee.department.name).

  List > list = repo.query( selects(select("department", "name")), eq("lastName", "Hightower")); assertEquals("engineering", list.get(0).get("department.name")); 

Ce qui précède essaierait d’utiliser les champs des classes. Si vous souhaitez utiliser les propriétés réelles (emp.getFoo () vs emp.foo), vous devez utiliser le selectPropertyPath.

  List > list = repo.query( selects(selectPropPath("department", "name")), eq("lastName", "Hightower")); 

Notez que select (“department”, “name”) est beaucoup plus rapide que selectPropPath (“department”, “name”), ce qui pourrait être important dans une boucle serrée.

Par défaut, tous les index de recherche et tous les index de recherche autorisent les doublons (à l’exception de l’index de clé primaire).

  repoBuilder.primaryKey("ssn") .searchIndex("firstName").searchIndex("lastName") .searchIndex("salary").searchIndex("empNum", true) .usePropertyForAccess(true); 

Vous pouvez remplacer cela en fournissant un indicateur vrai comme second argument à searchIndex.

Notez que empNum est un index unique consultable.

Si vous préférez ou avez besoin, vous pouvez même effectuer des recherches simples sous forme de cartes:

  List> employees = repo.queryAsMaps(eq("firstName", "Diana")); 

Je ne suis pas sûr s’il s’agit d’une fonctionnalité ou d’une erreur. Je pensais qu’une fois que vous traitez des données, vous devez présenter ces données d’une manière qui ne lie pas les consommateurs de données à votre API réelle. Avoir une carte de types Ssortingng / basic semble être un moyen d’y parvenir. Notez que l’object à cartographier la conversion est profond comme dans:

  System.out.println(employees.get(0).get("department")); 

Rendements:

 {class=Department, name=engineering} 

Cela peut être utile pour le débogage et les requêtes ad hoc pour les outils. J’envisage d’append un support pour convertir facilement en chaîne JSON.

Ajout de la possibilité d’interroger les propriétés de collection. Cela devrait fonctionner avec des collections et des tableaux aussi profondément nesteds que vous le souhaitez. Lisez à nouveau cela parce que c’était une vraie MF à mettre en œuvre!

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

L’impression de ce qui précède ressemble à ceci:

 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]}, ... 

J’ai créé plusieurs classes de relations pour tester ceci:

 public class Employee { List  tags = new ArrayList<>(); { tags.add(new Tag("tag1")); tags.add(new Tag("tag2")); tags.add(new Tag("tag3")); } ... public class Tag { ... List metas = new ArrayList<>(); { metas.add(new Meta("mtag1")); metas.add(new Meta("mtag2")); metas.add(new Meta("mtag3")); } } public class Meta { ... List metas2 = new ArrayList<>(); { metas2.add(new Meta2("2tag1")); metas2.add(new Meta2("2tag2")); metas2.add(new Meta2("2tag3")); } } ... public class Meta2 { List metas3 = new ArrayList<>(); { metas3.add(new Meta3("3tag1")); metas3.add(new Meta3("3tag2")); metas3.add(new Meta3("3tag3")); } public class Meta3 { ... 

Vous pouvez également rechercher par type:

  List results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee")); assertEquals(1, results.size()); assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName()); 

Le ci-dessus trouve tous les employés avec le nom de classe simple de SalesEmployee. Il fonctionne également avec le nom de classe complet comme dans:

  List results = sortedQuery(queryableList, "firstName", typeOf("SalesEmployee")); assertEquals(1, results.size()); assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName()); 

Vous pouvez également rechercher par classe:

  List results = sortedQuery(queryableList, "firstName", instanceOf(SalesEmployee.class)); assertEquals(1, results.size()); assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName()); 

Vous pouvez également interroger des classes implémentant certaines interfaces:

  List results = sortedQuery(queryableList, "firstName", implementsInterface(Comparable.class)); assertEquals(1, results.size()); assertEquals("SalesEmployee", results.get(0).getClass().getSimpleName()); 

Vous pouvez également indexer des champs / propriétés nesteds et ils peuvent être des champs de collection ou des champs de collection sans propriété aussi profondément nesteds que vous le souhaitez:

  /* 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"); 

Plus tard, vous pouvez utiliser nestedIndex pour effectuer une recherche.

  List> list = repo.query( selects(select("tags", "metas", "metas2", "name2")), eqNested("2tag1", "tags", "metas", "metas2", "name2")); 

Le moyen sûr d’utiliser nestedIndex est d’utiliser eqNested. Vous pouvez utiliser eq, gt, gte, etc. si vous avez l’index comme ceci:

  List> list = repo.query( selects(select("tags", "metas", "metas2", "name2")), eq("tags.metas.metas2.name2", "2tag1")); 

Vous pouvez également append un support pour les sous-classes

  List queryableList = $q(h_list, Employee.class, SalesEmployee.class, HourlyEmployee.class); List 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()); 

Le référentiel de données a une fonctionnalité similaire dans sa méthode DataRepoBuilder.build (…) pour spécifier des sous-classes. Cela vous permet d’inclure des champs de requête de sous-classes et de classes dans la même collection de référentiels ou de recherches.

J’ai écrit une interface de table qui comprend des méthodes comme

 V put(R rowKey, C columnKey, V value) V get(Object rowKey, Object columnKey) Map column(C columnKey) Set columnKeySet() Map row(R rowKey) Set rowKeySet() Set> 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 objective principal semble être de supprimer l’object de tous les index lorsque vous le supprimez.

L’approche la plus simple consiste à append un autre niveau d’indirection: vous stockez votre object réel dans une Map et utilisez une carte bidirectionnelle (que vous trouverez dans Jakarta Commons et probablement Google Code) pour vos index sous forme de Map . 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 est de définir vos cartes “index” comme Map> ; Cependant, vous devrez implémenter un ReferenceQueue pour le nettoyage.


Une autre solution consiste à créer un object clé qui peut prendre un tuple arbitraire, définir sa méthode equals() pour qu’il corresponde à n’importe quel élément du tuple et l’utiliser avec un TreeMap . Vous ne pouvez pas utiliser un HashMap , car vous ne pourrez pas calculer un code de hachage basé sur un seul élément du tuple.

 public class MultiKey implements Comparable { 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 les tables de préfecture . Ils supportent autant d’indices que vous voulez, sont rapides (les index sont des TreeMaps), et ont de belles options de filtrage (filtres booléens? Pas de problème!). Aucune firebase database requirejse, testée avec de grands ensembles de données dans de nombreuses applications de visualisation d’informations.

Dans leur forme brute, ils ne sont pas aussi pratiques que les conteneurs standard (vous devez traiter les lignes et les 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.

Je ne suis pas sûr de comprendre la question, mais je pense que ce que vous demandez, c’est de multiples façons de mapper à partir de différentes clés uniques et de procéder à un nettoyage approprié lorsque la valeur disparaît.

Je vois que vous ne voulez pas rouler vous-même, mais il y a une composition assez simple de carte et de multimap (j’ai utilisé le multimap Guava ci-dessous, mais celui d’Apache devrait fonctionner aussi bien). J’ai une solution rapide et sale ci-dessous (ignoré les constructeurs, car cela dépend de quel type de carte sous-jacente / multimap 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 extends ForwardingMap{ Map delegate; Multimap reverse; @Override protected Map 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 m) { for (Entry 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 values() { return reverse.keySet(); } } 

la suppression est O (nombre de clés), mais tout le rest est du même ordre qu’une implémentation de carte typique (une mise à l’échelle plus constante, car vous devez également append des éléments au verso).

Je viens d’utiliser les clés d’ Object (devrait être correct avec les implémentations appropriées de equals() et hashCode() et la distinction de clé) – mais vous pourriez aussi avoir un type de clé plus spécifique.

regardons le projet http://code.google.com/p/multiindexcontainer/wiki/MainPage. Il s’agit d’une manière générale d’utiliser des cartes pour les getters JavaBean et d’effectuer des recherches sur des valeurs indexées. Je pense que c’est ce que vous recherchez. Essayons.

Fondamentalement, une solution basée sur plusieurs cartes de hachage serait possible, mais dans ce cas, toutes doivent ê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

Voici comment j’y parviens: en ce moment, seules les méthodes put, remove et get fonctionnent pour le repos dont vous avez besoin pour remplacer les méthodes souhaitées.

Exemple:

 MultiKeyMap 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 extends HashMap { private Map 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 keyArray = (List) key; for (Ssortingng 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(Ssortingng... keys) { Key singleKey = new Key(); for (Ssortingng key : keys) { singleKey.add(key); } return singleKey; } public class Key extends ArrayList { } } 

Si vous voulez plusieurs index sur vos données, vous pouvez créer et gérer plusieurs cartes de hachage ou utiliser une bibliothèque comme Data Store:

https://github.com/jparams/data-store

Exemple:

 Store 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 le magasin de données, vous pouvez créer des index insensibles à la casse et toutes sortes de choses intéressantes. Cela vaut la peine de vérifier.