Эффективный способ найти, содержит ли карта любой из ключей из списка/iterable

Мне нужно проверить, содержит ли карта какой-либо из ключей из списка, и если это произойдет, верните первое совпадающее значение. Наивный подход, который приходит на ум, состоит в том, чтобы сделать это в двух вложенных циклах:

Map fields = new HashMap();
fields.put("a", "value a");
fields.put("z", "value z");
String[] candidates = "a|b|c|d".split("|");
for (String key : fields.keySet()){
    for (String candidate : candidates) {
        if (key.equals(candidate)){
            return fields.get(key);
        }
    }
}

Есть ли более эффективный и эффективный способ, возможно, опираясь на стандартную библиотеку Java?

7
добавлено отредактировано
Просмотры: 1
de

9 ответы

for(String candidate : candidates) {
 if(fields.containsKey(candidate)) {
  return fields.get(candidate)
 }
}

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

22
добавлено

Конечно, что-то вроде:

for (String candidate : candidates) {
     String result = fields.get(key);
     if (result != null) {
         return result;
     }
}

Вышеупомянутое выполняет только поиск one карты для каждого ключа кандидата. Это позволяет избежать отдельного теста на присутствие плюс извлечение, так как извлечение несуществующего ключа просто даст вам нуль. Обратите внимание (спасибо Slanec ), что нулевое значение для действительного ключа неотличимо от несуществующего ключа для этого решения.

Я не совсем понимаю, почему вы выполняете преобразование дела, кстати.

19
добавлено
Интересно ... Я правильно понимаю, что идея состоит в том, чтобы использовать map.get() один раз вместо map.contains() + map.get() , потому что что позволит избежать второго поиска? Это здорово!
добавлено автор ccpizza, источник
@BrianAgnew: Получил! Я не беспокоюсь о null, так как ключи на карте уже проверены на null .
добавлено автор ccpizza, источник
@Slanec: Спасибо, это хорошо знать. Тем не менее, я не понимаю, почему кто-то хочет хранить null ключи на карте. Вы столкнулись с реальным вариантом использования ключей null ?
добавлено автор ccpizza, источник
Это так же эффективно, как и может, но может оказаться сложным, если null является допустимым значением в Map .
добавлено автор Petr Janeček, источник
@ccpizza В качестве побочного примечания, если вам понадобятся значения null и максимальная производительность (надеюсь, после проверки с помощью профайлера), вы можете преодолеть это, вставив фиктивный объект вместо нуль s. Таким образом, вы все равно можете выполнить один поиск, и если будет возвращен null , вы узнаете, что ключа там не было. Если ваш фиктивный объект будет возвращен, вы вернетесь null .
добавлено автор Petr Janeček, источник
@ccpizza На самом деле, да, один раз, я думаю. В приложении, где у нас не было явного контроля над содержимым хэшмапа, так как это была конфигурация пользователя, и нам нужно было зафиксировать случай, когда был выбран (но не изменен) параметр или был сброшен до значения по умолчанию. Во всяком случае, есть вопрос SO для этого.
добавлено автор Petr Janeček, источник
@ccpizza - это правильно. Примечание. Предупреждение Slanec re. нулевые значения на карте, хотя
добавлено автор Brian Agnew, источник
@NimChimpsky keepAll является конструктивным.
добавлено автор Peter Lawrey, источник

Мой прием:

Map fields = new HashMap();
fields.put("a", "value a");
fields.put("z", "value z");
String[] candidates = "a|b|c|d".split("|");
for (String candidate : candidates) {
    if (fields.containsKey(candidate)) {
        return fields.get(candidate);
    }
}
7
добавлено

В Java 8 это может быть:

boolean exists = Arrays.stream(candidates).anyMatch(fields::containsKey);

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

Если вы хотите узнать первое или любое, что вы можете использовать:

Arrays.stream(candidates).filter(fields::containsKey).findAny();

или

Arrays.stream(candidates).filter(fields::containsKey).findFirst();

Согласно @ Klapsa2503 ответ выше

6
добавлено

Пытаться

Set keySet = new HashSet(fields.keySet());    
keySet.retainAll(list);

поэтому keySet должен иметь все ключи от HashMap, которые указаны в списке

5
добавлено
@Slanec, да, зависит от того, хочет ли OP получить все ключи или только первый.
добавлено автор Nikolay Kuznetsov, источник
Это, я считаю, самый короткий путь. Однако он может быть не самым быстрым, так как цикл заканчивается при первом найденном результате, но это продолжает свою работу до конца.
добавлено автор Petr Janeček, источник

В Java 8 вы можете использовать это:

return candidates.stream()
            .filter(fields::containsKey)
            .findFirst()
            .map(fields::get)
            .orElse(null);
5
добавлено

Попробуйте

    List list= Arrays.asList(1, 2, 3);
    HashMap map = new HashMap();
    map.put(1, 1);
    map.put(3, 3);
    Set set = new HashSet(map.keySet());
    set.retainAll(list);
    System.out.println(set);
    Object e = set.isEmpty() ? null : set.iterator().next();
    System.out.println(e);

вывод

[1, 3]
1
2
добавлено
Map fields = new HashMap();
fields.put("a", "value a");
fields.put("z", "value z");
String[] candidates = "a|b|c|d".split("|");
List canList = Arrays.asList(candidates );
for (String key : fields.keySet()){

if (canList .contains(key)) {
return fields.get(key);
}

}
1
добавлено
Примечание : canList.contains является линейным по времени.
добавлено автор Srinivas, источник

Вы можете использовать один цикл, если вы предполагаете, что ключ карты уже в нижнем регистре, так же, как вы предполагаете, значения поиска находятся в нижнем регистре.

1
добавлено