Как красиво пересечь два набора из двух карт? - PullRequest
4 голосов
/ 08 апреля 2019

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

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

Теперь я должен предотвратить отключение свойства, которое в настоящее время 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 неправильно используется для вычисления пересечения.

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

Ответы [ 3 ]

7 голосов
/ 08 апреля 2019

Вы можете просто выбрать все пары ключ-значение, значение которых составляет true, а затем с помощью ключа проверить, является ли значение из карты «update» 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 ...
2 голосов
/ 08 апреля 2019

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

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 ...
}
0 голосов
/ 08 апреля 2019

Попробуйте anyMatch

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