Петли на картах для поиска дубликатов


Мой код выполняет следующие действия:

Есть два Mapс свойства. Мне нужно проверить, если свойства с таким же ключом и значением уже существует, и если они делают, то выбрасывать исключение.

    Map<String, Object> existingProperties = requires.get(requiresIndex).getProperties();
    for (Map.Entry<String, Object> property : properties.entrySet()) {
        if (existingProperties.containsKey(property.getKey())){
            if (existingProperties.get(property.getKey()) instanceof String && property.getValue() instanceof String ){
                String existingValue = (String) existingProperties.get(property.getKey());
                String newValue = (String) property.getValue();
                if (existingValue.equals(newValue)){
                    throw new Exception("Property " + property.getKey() + " is existsing with value " + newValue + "in requires " + requiresName);
                }
            }
        }
    }

Мы работаем на Java 8. Существует ли лучше/более короткий способ, чтобы написать это?



211
3
задан 25 января 2018 в 08:01 Источник Поделиться
Комментарии
4 ответа

ОК, так как это было предложено в комментариях, здесь идет


  1. Можно заменить на петли с Java 8 stream() из EntrySet() коллекция свойств карте.

  2. Вы можете заменить все вложенные операторы if с filter() вы можете иметь ваш выбор отдельно filter()для каждого, если заявление, или просто объединить их все вместе с && оператора.

  3. после filter()нужно сказать поток до конца (ака КЗ) найти первый элемент, который соответствует фильтру.

  4. В filter() операция возвращает Optional так как возможно, что ни один элемент удовлетворяет фильтра сказуемого. на вопрос, необходимо выполнить действие, только если совпадение найдено, так что вы можете добавить ifPresent() что берет Consumer который делает что-то для сопоставленного элемента и возвращает void. ifPresent() ничего не делает, если элемент удовлетворяет фильтра сказуемого.

Примечание: опубликовано кодексе не указано, как newValue и requiresName которые упоминаются в throw заявление объявлены. обратите внимание, что это должен быть финал, чтобы быть включенными в лямбда-выражение.

properties.entrySet().stream()
.filter(property ->
existingProperties.containsKey(property.getKey()) &&
existingProperties.get(property.getKey()) instanceof String &&
property.getValue() instanceof String &&
existingProperties.get(property.getKey()).equals(property.getValue()))
.findFirst()
.ifPresent(property -> {
throw new Exception("Property " + property.getKey() + " is existsing with value " + newValue
+ "in requires " + requiresName);
});

Причины, почему вы хотели бы использовать stream характеристика


  1. Фактический итерация по коллекции осталось в библиотеку поток, который может принять решение для оптимизации этого вместо последовательного контура.

  2. Код более кратким и ясным.

2
ответ дан 25 января 2018 в 08:01 Источник Поделиться

Шарон Бен Ашер имеет хороший ответ, используя потоки. Я хотел бы добавить некоторые предложения для исходного кода, так как новички часто не знакомы с потоками в Java.

У вас есть немного избыточности в коде, что означает, что код выполняется несколько раз, что может навредить читабельности, снизить производительность и привести к ошибкам. Для решения проблемы, вы можете сохранить результаты вызовов методов в переменных и просто получить доступ к нескольким переменным раза. Это также позволяет вам выдаст результат новое имя, и хотя это может сделать код немного больше в плане строк кода, это также может сделать его намного чище, и отдельные строки короче.

Самый важный пример-это призвание property.getKey() и property.getValue() несколько раз. Вместо этого, сделать это в начале цикла, и сохранить результаты в переменные.

for (Map.Entry<String, Object> property : properties.entrySet()) {
String key = property.getKey();
Object value = property.getValue();

Затем к ним в коде, что делает его легче увидеть, где использовались те же значения. Вы используете тот же метод вызова, чтобы открыть еще один элемент другой карте, так что вы снова можете использовать переменную, чтобы сохранить результат и сделать код понятнее и короче:

    if (existingProperties.containsKey(key)) {
Object existingObject = existingProperties.get(key);
if (existingObject instanceof String && value instanceof String) {
String existingValue = (String) existingObject;
String newValue = (String) value;

Мы можем улучшить читабельность при форматировании очень длинных строк (как долго конкатенации строк) на 2 или 3 линии, вводя новые строки, где мы можем разделить его на логические части:

            if (existingValue.equals(newValue)) {
throw new Exception("Property " + key +
" is existing with value " + newValue +
" in requires " + requiresName);
}

Кстати, там, кажется, был недостающего в начале строки "in requires "и опечатка в " is existsing with value ".

5
ответ дан 25 января 2018 в 11:01 Источник Поделиться

Ваш текущий код выглядит как большой блок черными символами. Мне трудно читать, так как есть очень много слов в нем. Поэтому моя первая идея была вставить этот код в IDE и пусть IDE превратить его в нечто более читаемым.

static void checkForDuplicates() {
Map<String, Object> existingProperties = requires.get(requiresIndex).getProperties();
for (Map.Entry<String, Object> property : properties.entrySet()) {
if (existingProperties.containsKey(property.getKey())) {
if (existingProperties.get(property.getKey()) instanceof String && property.getValue() instanceof String) {
String existingValue = (String) existingProperties.get(property.getKey());
String newValue = (String) property.getValue();
if (existingValue.equals(newValue)) {
throw new Exception("Property " + property.getKey() + " is existsing with value " + newValue + "in requires " + requiresName);
}
}
}
}
}

Я впервые завернул кусок кода в правильно названный способ. Это то, что вы должны сделать, прежде чем отправлять код здесь, чтобы сделать ясно, что ввод и откуда она берется.

Затем я отформатировал код, чтобы избавиться от несовместимых интервалов. Наблюдая за основной макет код не гуманный задач, поэтому IDE должен сделать это за вас.

Теперь код не компилируется. В строгом смысле, это может уже сделать свой вопрос вне темы для этого сайта, который требует от рабочего кода. И код, который не компилируется, не работает по определению.

После преобразования все неизвестные переменные в Параметры, код:

static void checkForDuplicates(
Map<String, Object> existingProperties,
Map<String, Object> properties,
String requiresName) throws Exception {

for (Map.Entry<String, Object> property : properties.entrySet()) {
if (existingProperties.containsKey(property.getKey())) {
if (existingProperties.get(property.getKey()) instanceof String && property.getValue() instanceof String) {
String existingValue = (String) existingProperties.get(property.getKey());
String newValue = (String) property.getValue();
if (existingValue.equals(newValue)) {
throw new Exception("Property " + property.getKey() + " is existsing with value " + newValue + "in requires " + requiresName);
}
}
}
}
}

До сих пор никаких улучшений на удобочитаемость. Давайте спросим язь, может ли он сделать этот код более красивым. Идя по карте, как известно, требует много кода на Java, до Java 8. Но сначала давайте сделать код немного легче извлечь key и value для переменных:

static void checkForDuplicates(
Map<String, Object> existingProperties,
Map<String, Object> properties,
String requiresName) throws Exception {

for (Map.Entry<String, Object> property : properties.entrySet()) {
String key = property.getKey();
Object value = property.getValue();

if (existingProperties.get(key) instanceof String && value instanceof String) {
String existingValue = (String) existingProperties.get(key);
String newValue = (String) value;
if (existingValue.equals(newValue)) {
throw new Exception("Property " + key + " is existsing with value " + newValue + "in requires " + requiresName);
}
}
}
}

Это становится лучше. Черный код медленно исчезает, делая место для более цветов.

Следующая вещь заключается в замене throw new Exception С throw new IllegalStateException, поскольку последние не должны быть явно объявлены в методе определения (в throws Exception) и дает хороший рефакторинг в следующем шаге.

И сейчас IDE (в моем случае IntelliJ) может автоматически преобразовать этот большой код в эквивалентный код короче. Для этого наведите курсор на for сайта, нажмите сочетание клавиш Alt+Enter и выберите заменить на карте.по каждому элементу:

static void checkForDuplicates(
Map<String, Object> existingProperties,
Map<String, Object> properties,
String requiresName) {

properties.forEach((key, value) -> {
if (existingProperties.get(key) instanceof String && value instanceof String) {
String existingValue = (String) existingProperties.get(key);
String newValue = (String) value;
if (existingValue.equals(newValue)) {
throw new IllegalStateException("Property " + key + " is existsing with value " + newValue + "in requires " + requiresName);
}
}
});
}

Это уже помогло. Вместо двух абзацев кода, есть только один пункт влево.

В следующий шаг, экстракт вызов existingProperties.get(key) в переменную (сочетание клавиш Ctrl+АЛТ+в), назвав его existingValue. Это дает ошибку при компиляции, потому что есть уже другая переменная с тем же именем. Нам не нужны две переменные внутри if п., Так что просто удалить их и заменить их на внешние переменные:

static void checkForDuplicates(
Map<String, Object> existingProperties,
Map<String, Object> properties,
String requiresName) {

properties.forEach((key, value) -> {
Object existingValue = existingProperties.get(key);
if (existingValue instanceof String && value instanceof String) {
if (existingValue.equals(value)) {
throw new IllegalStateException("Property " + key + " is existsing with value " + value + "in requires " + requiresName);
}
}
});
}

Текст содержит исключений отсутствует пробел перед in requires, но это не так легко увидеть. Чтобы сделать этот код более читаемым, пусть IDE заменить + операторы с String.format поместив курсор в строку и нажав клавиши Alt+ввод. Из меню выберите пункт заменить '+' с 'строки.формат:

static void checkForDuplicates(
Map<String, Object> existingProperties,
Map<String, Object> properties,
String requiresName) {

properties.forEach((key, value) -> {
Object existingValue = existingProperties.get(key);
if (existingValue instanceof String && value instanceof String) {
if (existingValue.equals(value)) {
throw new IllegalStateException(String.format("Property %s is existsing with value %sin requires %s", key, value, requiresName));
}
}
});
}

Строки, содержащей исключение будет еще очень долго, поэтому извлечь исключение сообщение в переменную (сочетание клавиш Ctrl+клавиши Alt+В) и отформатировать его красиво. Затем, заменить if условия с более коротким, эквивалентной:

static void checkForDuplicates(
Map<String, Object> existingProperties,
Map<String, Object> properties,
String requiresName) {

properties.forEach((key, value) -> {
Object existingValue = existingProperties.get(key);
if (existingValue instanceof String && Objects.equals(existingValue, value)) {
String message = String.format(
"Property %s is existsing with value %sin requires %s",
key, value, requiresName);
throw new IllegalStateException(message);
}
});
}

Заключительным этапом является фиксация опечатки (existing и in) в сообщение исключения. Потом вы закончите:

static void checkForDuplicates(
Map<String, Object> existingProperties,
Map<String, Object> properties,
String requiresName) {

properties.forEach((key, value) -> {
Object existingValue = existingProperties.get(key);
if (existingValue instanceof String && Objects.equals(existingValue, value)) {
String message = String.format(
"Property %s is existing with value %s in requires %s",
key, value, requiresName);
throw new IllegalStateException(message);
}
});
}

2
ответ дан 28 января 2018 в 10:01 Источник Поделиться

Понимание String.equals(Object)

Так String.equals(Object) не может быть переопределен, это безопасно, чтобы просто проверить, что записи' значение String. Другими словами, вам нужно лишь instanceof String проверить.

Перебирает мелкие петли

Сейчас, сейчас, это попахивает немного преждевременная оптимизация, но если вы уверены в том, что один из Map значительно меньше, чем другие, и, вероятно, оставаться таким образом, вы можете рассмотреть итерации на более мелкие петли.

Map.remove(Object, Object)

Потоки немного сложнее понять, но я не думаю, что это означает, что усилия не должны быть созданы ;). Есть немного короче, и поэтому, возможно, легче понять, решение, которое основывается на языке Java 8 Новая Map.remove(Object, Object) способ сделать запись значения сравнения для нас тоже.

Map<String, Object> copy = new HashMap<>(requires.get(requiresIndex).getProperties());
Optional<Map.Entry<String, Object>> duplicate = properties.entrySet().stream()
.filter(property -> property.getValue() instanceof String
&& copy.remove(property.getKey(), property.getValue()))
.findFirst();
if (duplicate.isPresent()) {
Map.Entry<String, Object> property = duplicate.get();
throw new Exception("Property " + property.getKey() + " exists with value "
+ property.getValue() + " in requires " + requiresName);
}

При таком подходе, второй фильтрации условие проверяет, если copy этого требует схема содержит те же записи, которые будут удалены от входа в properties. Первая фильтрация условие является обязательным для обеспечения по меньшей мере одного из значения Stringза ваш вопрос.

Все-таки, это предполагает, что все записи, значения будут строго придерживаться общему контракту Object.equals(Object)т. е. там не будет случаем MyType.equals(String) == trueString.equals(MyType) == false).

Вы должны также рассмотреть возможность использования более правильно набран Exception класс вместо проверено Exception. Здесь, лямбды не справиться с проверено исключения, так что я использовал RuntimeException в качестве примера.

редактировать

Так как вы хотите, чтобы также войти все дубликаты ключей перед запуском проверка значений, можно вставить Stream.peek(Consumer) шаг между вашим (теперь два) фильтра шагов:

Optional<Map.Entry<String, Object>> duplicate = properties.entrySet().stream()
.filter(property -> property.getValue() instanceof String
&& copy.containsKey(property.getKey()))
.peek(property -> LOG.warn("Property with key {} already exists in requires {}",
property.getKey(), requires.get(requiresIndex).getName()))
.filter(property -> copy.remove(property.getKey(), property.getValue()))
.findFirst();

1
ответ дан 27 января 2018 в 12:01 Источник Поделиться