#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));