Сборщики StreamAPI.группировка по пустой коллекции вместо null

#java #java-8 #java-stream

#java #java-8 #java-поток

Вопрос:

 List<Entity> entities = ...
Map<Boolean, List<Entity>> entitiesByIsTest = entities.stream()
                        .collect(Collectors.groupingBy(Entity::isTest));
  

Очевидно, что результирующая карта имеет единственные ключи, которые присутствуют в свойстве группировки. Это должно работать так для типов с бесконечным набором значений. Но как насчет перечислений / логических значений / других определенных типов?

Можно ли реализовать инициализацию пустых коллекций более элегантно, чем в приведенном ниже фрагменте?

 if (entitiesByIsTest.get(true) == null) {
    entitiesByIsTest.put(true, new ArrayList());
}
  

Комментарии:

1. трудно понять вопрос. какова цель вашего кода?

2. Цель состоит в том, чтобы разделить набор на два независимых набора, а затем выполнить несколько разных действий — например, сохранить обычные объекты, но сохранить только количество тестовых объектов

Ответ №1:

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

 Map<Boolean, List<Entity>> entitiesByIsTest = entities.stream()
                        .collect(Collectors.partitioningBy(Entity::isTest));
  

Если ключом является an enum , вы должны остаться groupingBy , но вы можете заменить последующие get операции на

 List<Entity> value=map.computeIfAbsent(key, x->new ArrayList<>());
  

который создаст и поместит новое ArrayList тогда и только тогда, когда предыдущего сопоставления не было, и вернет фактическое сопоставленное значение в любом случае (в отличие putIfAbsent от ).

Конечно, вместо этого вы могли бы добавить все отсутствующие значения в одну нетерпеливую операцию:

 EnumSet.allOf(KeyType.class).forEach(key->map.computeIfAbsent(key, x->new ArrayList<>()));
  

Ответ №2:

Вы могли бы использовать entitiesByIsTest.putIfAbsent(true,new ArrayList()); это, чтобы добавить новый пустой список массивов только тогда, когда в map для этого ключа ничего нет

Комментарии:

1. Спасибо! хороший момент. Одно дело, что это не функциональный стиль. Моя главная цель — использовать все преимущества stream api, такие как понятный код, максимально избегая состояний

2. Совсем не понятно, о чем вы просите. У вас уже есть решение для этой groupingBy вещи — это уже работает для перечислений, логических значений или любых других типов. Этот ответ отвечает на ваш второй вопрос: как инициализировать пустые коллекции на карте.

Ответ №3:

Вы можете написать свой собственный коллектор.

 Collectors.of(
   //**Initialize map with all of your values**//,
   entity -> map.get(entity.isTest()).add(entity),
   (left, right) -> right.forEach(r -> left.get(r.getKey()).addAll(r.getValue)); return r;)
  

Ответ №4:

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

Это должно группироваться по перечисляемым элементам:

 Map<Type, List<Value>> emptyMap = EnumSet.allOf(Type.class)
                .stream()
                .collect(toMap(identity(), f -> new ArrayList<>()));
Function<Value, Type> fun = this::groupByType;
Map<Type, List<Value>> groups = values.stream()
                .collect(groupingBy(fun, () -> emptyMap, toList()));