Научитесь фильтровать Map по ключам или значениям или и по тому, и по другому, используя методы Java 8 Streamfilter() и collect(). Эти простые в использовании фрагменты кода направлены на написание общих функций, которые мы можем использовать для фильтрации любой Map по ее ключам или значениям.
Для демонстрационных целей мы используем следующую карту:
Map<Integer, User> usersMap = Map.of(1, new User(1, "Alex"),2, new User(2, "Allen"),3, new User(3, "Brian"),4, new User(4, "Bob"),5, new User(5, "Charles"),6, new User(6, "David"),7, new User(7, "Don"),8, new User(8, "Dave"));
Пользователь — это простой тип записи с двумя компонентами: идентификатором и именем.
record User(Integer id, String name) {}
1. Фильтрация карты по ключам
Ниже приведен общий синтаксис для фильтрации Map по ключам и сбора записей в новую Map. Все, что нам нужно сделать, это предоставить условие, используемое для фильтрации.
map.entrySet().stream().filter(key -> {condition}).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
В следующем примере мы создали общую функцию, которую будем использовать для фильтрации записей из демонстрационной карты. Обратите внимание, что для того, чтобы сделать функцию общей, мы облегчили передачу условия как Predicate.
public static final class Filters {private Filters() {throw new AssertionError("Cannot be instantiated");}public static <K, V> Map<K, V> byKey(Map<K, V> map, Predicate<K> predicate) {Objects.requireNonNull(map);Objects.requireNonNull(predicate);return map.entrySet().stream().filter(item -> predicate.test(item.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));}//...}
Теперь предположим, что мы хотим отфильтровать и собрать все записи карты, где значение ключа больше 4(key > 4). Тогда мы можем использовать функцию Filters.byKey() следующим образом:
Predicate<Integer> predicate = key -> key > 4;Map<Integer, User> filteredMap = Filters.byKey(usersMap, predicate);System.out.println(filteredMap);
Вывод программы:
{5=User[id=5, name=Charles], 6=User[id=6, name=David], 7=User[id=7, name=Don], 8=User[id=8, name=Dave]}
2. Фильтрация карты по значениям
Далее мы модифицировали предыдущий метод для поддержки фильтрации на основе значений входа. На этот раз внутри Stream.filter() мы применяем условие к значению входа.
public static final class Filters {//...public static <K, V> Map<K, V> byValue(Map<K, V> map, Predicate<V> predicate) {Objects.requireNonNull(map);Objects.requireNonNull(predicate);return map.entrySet().stream().filter(item -> predicate.test(item.getValue())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));}}
Давайте проверим этот метод, отфильтровав карту по всем значениям, начинающимся с «D».
Predicate<User> valuePredicate = user -> user.name().startsWith("D");Map<Integer, User> filteredMapByValue = Filters.byValue(usersMap, valuePredicate);System.out.println(filteredMapByValue);
Вывод программы:
{6=User[id=6, name=David], 7=User[id=7, name=Don], 8=User[id=8, name=Dave]}
3. Фильтрация карты по ключам и значениям, оба варианта
Иногда нам может понадобиться использовать сложные условия, так что фильтрация должна применяться как к ключам, так и к значениям. В таких случаях у нас есть два варианта:
- Запишите сложное условие в методе filter() явно.
- Свяжите методы Filters.byKey() и Filters.byValue().
Предположим, мы хотим отфильтровать Map по ключам и значениям, так что ключ больше 4, а значение начинается с «D». Мы можем связать универсальный метод следующим образом:
Predicate<Integer> predicate = key -> key > 4;Predicate<User> valuePredicate = user -> user.name().startsWith("D");Map<Integer, User> filteredMapByKeyAndValue = Filters.byValue(Filters.byKey(usersMap, predicate), valuePredicate);System.out.println(filteredMapByKeyAndValue);
Вывод программы:
{8=User[id=8, name=Dave], 6=User[id=6, name=David], 7=User[id=7, name=Don]}
Аналогично мы можем записать явное условие в методе фильтра следующим образом:
Map<Integer, User> filteredMapByKeyAndValue_V2 = usersMap.entrySet().stream().filter((entry) -> entry.getKey() > 4 && entry.getValue().name().startsWith("D")) // both conditions.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));System.out.println(filteredMapByKeyAndValue);
Вывод программы:
{6=User[id=6, name=David], 7=User[id=7, name=Don], 8=User[id=8, name=Dave]}
4. Заключение
В этом кратком руководстве обсуждалось, как фильтровать Map по ключам, значениям или обоим с помощью API потока Java 8. Эти универсальные функции могут помочь вам извлечь общую функциональность в методах утилит, тем самым делая код более чистым и поддерживаемым.