6
votes

Recherchez plusieurs hachages en même temps

TLDR : Comment puis-je rechercher une entrée dans plusieurs hachage Java (en lecture seule) en même temps?


La version longue:

J'ai plusieurs dictionnaires de différentes tailles stockées comme hashmap . Une fois qu'ils sont lus, ils ne doivent jamais être changés (strictement en lecture seule). Je veux vérifier si et quel dictionnaire avait stocké une entrée avec ma clé.

Mon code cherchait à l'origine une clé comme celle-ci: xxx

alors Il a un peu plus compliqué: ma chaîne de recherche pourrait contenir des fautes de frappe ou une variante de l'entrée stockée. Comme si la clé stockée était "banane", il est possible que je rechercherais "Bannana" ou "une banane", mais j'aimerais toujours que l'entrée de "banane" est revenue. À l'aide de la distance de Levenshtein, je bouconne maintenant tous les dictionnaires et chaque entrée en eux: xxx

jusqu'à présent, tout fonctionne comme il devrait et je reçois l'entrée que je veux. Malheureusement, je dois regarder environ 7 000 cordes, dans cinq dictionnaires de différentes tailles (~ 30 - 70k entrées) et il faut un moment. De ma sortie de traitement, j'ai la forte impression que ma recherche domine l'exécution globale.

Ma première idée à améliorer l'exécution était de rechercher tous les dictionnaires parallèles. Comme aucun des dictionnaires ne doit être changé et que plus d'un thread accède à un dictionnaire en même temps, je ne vois aucune préoccupation de sécurité.

La question est juste: comment puis-je faire cela? Je n'ai jamais utilisé de multithreading avant. Ma recherche ne propose que des hashmaps simultanés (mais à ma compréhension, je n'ai pas besoin de cela) et de la classe annoncable, où je devrais mettre mon traitement dans la méthode exécution () . Je pense que je pouvais réécrire ma classe actuelle pour s'intégrer à courir, mais je me demandais s'il y a peut-être une méthode plus simple pour le faire (ou comment puis-je le faire simplement avec runnable, à présent, ma compréhension limitée pense que je dois restructurer beaucoup ))


4 commentaires

Je suggérerais d'enquêter sur une meilleure partitionnement des données. Cela ressemble à un bon travail pour une structure trie.


En pensant aux arbres, je suppose que vous voulez dire que si vous recherchez "banane", je ne voudrais que les entrées commençant par "B", non? Mais que si ma clé est "une banane"? Comment devrais-je obtenir des hits?


Souhaitez-vous fournir votre distance de Levenshtein distance logique? Peut-être que cela aiderait à réduire le temps d'exécution


@Babel: J'ai édité mon texte et j'ai ajouté la distance de Levenshtein. Je n'ai pas écrit le calcul moi-même, mais il suffit d'utiliser stringutils .


5 Réponses :


2
votes

Pour utiliser la multi-threading dans votre cas, cela pourrait être quelque chose comme:

la classe "Monitor", qui stocke essentiellement les résultats et coordonne les threads; P>

public static void main(String[] args) throws Exception {

    Results results = new Results();

    ThreadDictionarySearch threadA = new ThreadDictionarySearch(results, "dictionary A");
    ThreadDictionarySearch threadB = new ThreadDictionarySearch(results, "dictionary B");
    ThreadDictionarySearch threadC = new ThreadDictionarySearch(results, "dictionary C");
    ThreadDictionarySearch threadD = new ThreadDictionarySearch(results, "dictionary D");

    threadA.start();
    threadB.start();
    threadC.start();
    threadD.start();

    if (results.isReady())
    // it stays here until all dictionaries are searched
    // because in "Results" it's told to wait() while not finished;

for (String string : results.getAllResults()) {
        System.out.println("RESULT: " + string);
    }


9 commentaires

S'il veut couvrir des fautes de frappe, cela ne fonctionnera pas. "ZBanana" est plus semblable à "banane" que "BASFDFSDFSDF", mais sera plus loin dans la carte triée ...


Le dictionnaire (vient dans un fichier texte) est censé être trié déjà.


Treemap ne pas itérair à chaque entrée et triéMAP est également thread-coffre-fort;


De ce que je me souviens des arbres et des cartes des arbres, si vous recherchez "banane", je voudrais seulement / d'abord envisager des entrées commençant par "B", non? Mais comment procéder si je cherchais une "banane" ou @Cichystefan a suggéré "ZBanana" et des entrées dans "A" ou "Z" ne donneraient aucun résultat? Devrais-je faire boucle à travers toutes les entrées restantes?


Oui. Si vous pouvez utiliser une structure triée de tout type, vous pouvez régler les chaînes de leur longueur et se concentrer sur la recherche uniquement des entrées avec une longueur similaire dans cette structure ...


mon mauvais .. j'ai édité le commentaire, donc je démontre comment vous pourriez utiliser multi-threading pour la recherche;


Merci pour le code exemple. Bien que cela semble assez simple, je ne peux toujours pas envelopper ma tête autour de lui. J'ai essayé d'installer mon code à l'intérieur, mais pendant l'exécution, je reçois un illegalGalMonitorStateException . J'ai donc essayé d'appeler synchronisé (dict) dans mon principal avant de commencer la recherche et que l'exception disparaît, mais il semble que fait n'atteint jamais 0 . Pouvez-vous peut-être expliquer la recherche multi-threads sur un exemple plus général? Comme peut-être simplement laisser chaque fil compter et imprimer un nombre (au lieu de chercher une carte)? Je pense que mon problème est plutôt comment la configurer: -s


Hé, fukiburi, c'est fait :) Vous pouvez le vérifier maintenant, 3 classes, aucune erreur; Il suffit de me souvenir de la façon dont les threads et les moniteurs travaillent;


Merci beaucoup! : D exactement, ce que je cherchais. Désolé pour accepter tardif, était un peu occupé avec d'autres choses.



0
votes

Je pense que le plus facile serait d'utiliser un flux sur le jeu d'entrée:

public DictionaryEntry getEntry(String key) {
  for (int i = 0; i < _numDictionaries; i++) {
    HashMap<String, String> map = getDictionary(i);

    map.entrySet().parallelStream().foreach( (entry) ->
                                     {
                                       // Calculate Levenshtein distance, store closest match etc.
                                     }
      );
  }
  // return closest match or null.
}


0 commentaires

0
votes

Peut-être essayez peut-être des piscines de thread:

ExecutorService es = Executors.newFixedThreadPool(_numDictionaries);
for (int i = 0; i < _numDictionaries; i++) {
    //prepare a Runnable implementation that contains a logic of your search
    es.submit(prepared_runnable);
}


0 commentaires

0
votes

J'ai mes forts doutes que les hashmaps sont une solution appropriée ici, surtout si vous voulez avoir des mots de floue et d'arrêt. Vous devez utiliser une solution de recherche de texte complète appropriée comme elaticsearch ou Apache Solr ou au moins un moteur disponible comme Apache Lucene .

Cela étant dit, vous pouvez utiliser la version d'un homme pauvre: créer une matrice de vos cartes et une sorte de type, itérale sur la matrice, prenez les clés du hashmap actuel et de les stocker dans le type de type avec l'index de leur hashmap. Pour récupérer une clé, vous recherchez d'abord dans la sélection de cartes pour ladite clé, obtenez le hashmap respectif de la matrice à l'aide de la position de l'index et recherchez la clé dans un seul hashmap. Devrait être suffisamment rapide sans avoir besoin de multiples threads de creuser par les hachons. Cependant, vous pouvez rendre le code ci-dessous dans un fichier runnable et vous pouvez avoir plusieurs recherches en parallèle. xxx

Veuillez noter qu'il s'agit d'une mise en œuvre plutôt naïve que prévue à des fins d'illustration. Il ne traite pas de plusieurs problèmes (vous ne pouvez pas avoir des entrées d'index en double, par exemple).

Avec cette solution, vous négociez essentiellement une vitesse de démarrage pour la vitesse de requête.


3 commentaires

Étant donné que j'essaie encore des dictionnaires, j'ai l'impression d'ajouter Elasticsearch ou Solr semble un peu trop chers. Ce que je suis vraiment intéressé en ce moment, c'est simplement comment faire une chose indépendante parallèles.


@Fukiburi me pardonne, mais aussi loin que j'ai compris votre question, vous recherchiez un moyen efficace de rechercher des paires de clé / valeur provenant de multiples hachages en lecture seule. Pour moi, réinventer la roue semble être une overcilleuse;)


Haha, oui, selon le point de vue ou l'autre pourrait être surchargé. Mes dictionnaires et vos requêtes sont en fait assez simples. Il peut y avoir quelques erreurs dans la chaîne de recherche, mais la distance de Levenshtein est plus que suffisante pour couvrir cela (ici). L'objectif principal n'est actuellement pas la correspondance d'entrée parfaite, je veux juste améliorer l'exécution pour une expérimentation plus rapide.



0
votes

D'accord! Peut conserver trois dictionnaires d'un fil et de repos deux prendront soin par un autre fil. Et puis la sorcière jamais thread trouve le match arrêtera ou terminera l'autre thread.

Peut-être que vous avez besoin d'une logique supplémentaire pour faire ce travail de division ... mais cela n'exploitera pas votre temps de performance.

et peut-être que vous avez besoin de plus de changements supplémentaires dans votre code pour obtenir votre correspondance fermée: xxx

Vous utilisez entrée mais vous n'êtes pas En utilisant les valeurs de toute façon, il semble que le jeu d'entrée est un peu cher. Et je vous suggère de simplement utiliser Keyset car vous n'êtes pas vraiment intéressé par les valeurs dans cette carte xxx

Pour plus de détails sur le proformance de la carte, veuillez lire ce lien Performances de carte < / a>

L'itération sur la collection-Vues d'un LinkedHashMap nécessite du temps proportionnel à la taille de la carte, quelle que soit sa capacité. L'itération sur un hachemme est susceptible d'être plus chère, nécessitant du temps proportionnel à sa capacité.


1 commentaires

Merci pour les informations sur les performances de la carte. Je vais garder cela à l'esprit et repenser mon algorithme en général.