Проверьте, содержит ли хэш-карта другую хэш-карту с определенным значением в Java

#java #loops #hashmap #contains

Вопрос:

У меня есть такая хэш-карта:

 private HashMap<Integer, HashMap<String, Material>> logs = new HashMap<>();
 

Затем у меня есть несколько материалов, хранящихся в виде перечисления (например. Material.OAK_LOG ).

Есть ли простой способ проверить logs , содержит ли хэш-карта хэш-карту с определенным материалом?

Я придумал это, и это работает, но я хочу знать, есть ли какой-либо другой способ сделать это без перебора всей хэш-карты

 private boolean hasLog(Material mat){
    boolean contains = false;
    for (Map.Entry<Integer, HashMap<String, Material>> entry : this.logs.entrySet()) {
        if(entry.getValue().containsValue(mat)){
            contains = true;
            break;
        }
    }
    return contains;
}
 

Ответ №1:

Нет, вам придется перебирать карты, выполняя последовательный поиск.

Вы можете немного упростить логику , используя values() вместо entrySet() и просто return напрямую, но это всего лишь незначительный рефакторинг:

 private boolean hasLog(Material mat) {
    for (HashMap<String, Material> submap : this.logs.values())
        if (submap.containsValue(mat))
            returns true;
    return false;
}
 

Вы можете написать ту же логику, используя потоки Java 8 , но это тот же последовательный поиск вложенного цикла, поэтому сложность выполнения остается O(nm).

 private boolean hasLog(Material mat) {
    return this.logs.values().stream()
            .anyMatch(submap -> submap.containsValue(mat));
}
 

Ответ №2:

Если ваши Material объекты неизменяемы и уникальны с точки equals зрения, вы можете использовать их в качестве ключа на карте перекрестных ссылок. Но если материал изменится, ваши карты могут быть повреждены в зависимости от того, как настроен equals.

 
Map<Material, String> crossRef = new HashMap<>();
 

Всякий раз, когда вы добавляете новую карту с материалом в журналы, выполните следующие действия:

 int outerKey; = ... // some integer to get the inner map
String innerKey = .. // some string to get the actual Material
Map<String, Material> innerMap = logs.get(outerKey);
Material mat = new Material(...);
innerMap.put(innerKey, mat);
crossRef.put(mat, outerKey "_" innerKey);
 

Тогда позже

 if (crossRef.contains(mat)) {
   // it exists somewhere.
   String mapId = crossRef.get(mat);
   key[] parts = mapId.split("_");
   int outerKey = Integer.valueOf(parts[0]);
   String innerKey = parts[1];
   Map<String, Material> map = logs.get(outerKey);
   Material mat = map.get(innerKey);
}
 

Еще одним недостатком является то, что вы ускоряете время поиска за счет увеличения объема хранилища.

И еще раз подчеркнем, что если два разных материальных объекта будут сравниваться одинаково, они будут считаться дубликатами и, следовательно, не могут использоваться в качестве ключей для доступа к обоим типам материалов.

Вместо использования связанной строки в качестве ключа перекрестной ссылки вы можете использовать простой class или record содержащий их в качестве определенного типа.

Это был затянутый ответ на простой вопрос, но он может дать некоторые альтернативные идеи о том, как решить вашу проблему.