tldr: Как я могу искать запись в нескольких (только для чтения) Java HashMaps в одно и то же время?Поиск нескольких HashMaps в то же время
Длинная версия:
У меня есть несколько словарей различных размеров, хранящихся в HashMap< String, String >
. Когда они будут прочитаны, они никогда не будут изменены (строго для чтения). Я хочу проверить, был ли в каком словаре сохранена запись с моим ключом.
Мой код был изначально искал ключ, как это:
public DictionaryEntry getEntry(String key) {
for (int i = 0; i < _numDictionaries; i++) {
HashMap<String, String> map = getDictionary(i);
if (map.containsKey(key))
return new DictionaryEntry(map.get(key), i);
}
return null;
}
Тогда это стало немного сложнее: моя строка поиска может содержать опечатки, или был вариант хранимой записи. Например, если сохраненный ключ был «бананом», возможно, я бы поискал «баннану» или «банан», но все равно хотел бы, чтобы запись для «банана» вернулась. Использование Левенштейна-Distance, я теперь перебрать все словари и каждую запись в них:
public DictionaryEntry getEntry(String key) {
for (int i = 0; i < _numDictionaries; i++) {
HashMap<String, String> map = getDictionary(i);
for (Map.Entry entry : map.entrySet) {
// Calculate Levenshtein distance, store closest match etc.
}
}
// return closest match or null.
}
До сих пор все работает как надо, и я получаю запись я хочу. К сожалению, мне приходится искать около 7000 строк, в пяти словарях различного размера (~ 30 - 70 тыс. Записей), и это занимает некоторое время. Из моего результата обработки у меня сильное впечатление, что мой поиск доминирует в общей среде исполнения.
Моя первая идея улучшить время выполнения - это искать все словари параллельно. Поскольку ни один из словарей не должен меняться, и не более одного потока одновременно обращается к словарю, я не вижу никаких проблем с безопасностью.
Вопрос просто: как это сделать? Я никогда раньше не использовал многопоточность. В моем поиске появился Concurrent HashMaps (но, на мой взгляд, мне это не нужно) и Runnable-class, где мне пришлось бы поместить мою обработку в метод run()
. Я думаю, что я мог бы переписать мой текущий класс, чтобы он поместился в Runnable, но мне было интересно, может быть, есть более простой способ сделать это (или как я могу это сделать просто с Runnable, сейчас мое ограниченное понимание думает, что мне нужно реструктурировать много).
С меня попросили поделиться Левенштейна-Logic: Это на самом деле ничего особенного, но здесь вы идете:
private int _maxLSDistance = 10;
public Map.Entry getClosestMatch(String key) {
Map.Entry _closestMatch = null;
int lsDist;
if (key == null) {
return null;
}
for (Map.Entry entry : _dictionary.entrySet()) {
// Perfect match
if (entry.getKey().equals(key)) {
return entry;
}
// Similar match
else {
int dist = StringUtils.getLevenshteinDistance((String) entry.getKey(), key);
// If "dist" is smaller than threshold and smaller than distance of already stored entry
if (dist < _maxLSDistance) {
if (_closestMatch == null || dist < _lsDistance) {
_closestMatch = entry;
_lsDistance = dist;
}
}
}
}
return _closestMatch
}
Я бы предложил исследовать лучшее разбиение данных. Это похоже на хорошую работу для структуры Trie. –
Если вы думаете о деревьях, я полагаю, вы имеете в виду, что если вы ищете «банан», я бы рассматривал только записи, начинающиеся с «B», правильно? Но что, если мой ключ «банан»? Как мне получить какие-то хиты? – fukiburi
Вы хотите предоставить логику 'Levenshtein distance'? Может быть, это поможет сократить время работы – Babel