Как красиво соединить два набора, построенных из двух карт?

#java #dictionary #java-8 #set #java-stream

#java #словарь #java-8 #установить #java-поток

Вопрос:

Наши объекты имеют «свойства»; и их текущее состояние представлено так Map<String, Object> , где ключ напоминает имя свойства. Значения могут иметь разные типы, однако моя текущая задача касается только логических свойств.

Помимо текущего состояния, с помощью таких карт организуются также «обновления» объектов.

Теперь я должен предотвратить отключение свойства, которое в данный момент true отключено (включено false ).

Используя потоки, это здесь работает:

 Set<String> currentlyEnabled = currentObjectPropertiesMap.
            .entrySet()
            .stream()
            .filter(e -> Boolean.TRUE.equals(e.getValue()))
            .map(Entry::getKey)
            .collect(Collectors.toSet());

Set<String> goingDisabled = updatedObjectPropertiesMap
        .entrySet()
        .stream()
        .filter(e -> Boolean.FALSE.equals(e.getValue()))
        .map(Entry::getKey)
        .collect(Collectors.toSet());

currentlyEnabled.retainAll(goingDisabled);

if (currentlyEnabled.isEmpty()) {
    return;
} else {
  throw new SomeExceptionThatKnowsAllBadProperties(currentlyEnabled);
}
  

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

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

Любое предложение, как это можно сделать более идиоматичным, но читаемым «потоковым» способом?

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

1. Почему бы не currentObjectPropertiesMap.entrySet() повторить итерацию по continue записям, в которых указаны значения false , в противном случае проверьте, верно ли updatedObjectPropertiesMap.hasKey() это, и если это правда, то выдайте исключение, если значение для этого ключа равно false ?

Ответ №1:

Вы можете просто выбрать все пары ключ-значение, значение которых равно true , а затем с помощью ключа проверить, соответствует ли значение из «update»-map false .

 Set<String> matches = currentObjectPropertiesMap
    .entrySet()
    .stream()
    .filter(e -> Boolean.TRUE.equals(e.getValue()))
    .map(Map.Entry::getKey)
    .filter(k -> Boolean.FALSE.equals(
        updatedObjectPropertiesMap.get(k)
    ))
    .collect(Collectors.toSet());

if(!matches.isEmpty()) throw ...
  

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

1. Можно объединить два filter s в один, а затем map . Хотя и не большая разница .filter(e -> Boolean.TRUE.equals(e.getValue()) amp;amp; Boolean.FALSE.equals(updatedObjectPropertiesMap.get(e.getKey()))) .map(Map.Entry::getKey)

2. @Naman Я согласен, но IMO, потоковые операторы должны быть как можно более простыми. Мое эмпирическое правило — одно условие / оператор на filter / map -операцию (если возможно), но, как уже было сказано, это всего лишь мнение

3. Для эффективности вы можете проверить, какая карта меньше, и выполнить итерацию по меньшей, т. Е. Если updatedObjectPropertiesMap меньше, используйте Set<String> matches = updatedObjectPropertiesMap .entrySet() .stream() .filter(e -> Boolean.FALSE.equals(e.getValue())) .map(Map.Entry::getKey) .filter(k -> Boolean.TRUE.equals(currentObjectPropertiesMap.get(k))) .collect(Collectors.toSet()); вместо этого; результат будет тот же.

Ответ №2:

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

 Set<String> violatingProperties = new HashSet<String>();
for (Entry<String, Object> entry : currentObjectPropertiesMap.entrySet()) {
    if (! (Boolean) entry.getValue()) {
        continue;
    }
    if (! updatedObjectPropertiesMap.hasKey(entry.getKey())) {
        continue;
    }
    if (! (Boolean) updatedObjectPropertiesMap.get(entry.getKey())) {
        violatingProperties.add(entry.getKey());
    }
}
if (violatingProperties.size() > 0) {
    throw ...
}
  

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

1. Проблема в том, что я А) предпочитаю потоковое решение и Б) мне нужно «собрать» все имена свойств, которые нарушают мое условие.

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

Ответ №3:

Попробуйте anyMatch

 boolean anyMatch = currentXXXMap.entrySet()
    .stream()
    .anyMatch(e -> e.getValue() amp;amp; !updatedXXXMap.getOrDefault(e.getKey(), true));