Объединить две карты и сохранить нулевые значения

Я надеюсь, что у тебя все хорошо.

У меня есть две карты, и я хочу сохранить нулевые значения. Я знаю, что функция слияния не может управлять нулевыми значениями, но мне нужно сохранить нулевые данные и использовать двоичную операцию.

Как я могу управлять своим кодом, чтобы избежать NPE.

  private Map<String, Object> mergeMaps(
      Map<String, Object> map1, Map<String, Object> map2) {
    return Stream.of(map1, map2)
        .map(Map::entrySet)
        .flatMap(Collection::stream)
        .collect(
            Collectors.toMap(
                Map.Entry::getKey,
                Map.Entry::getValue,
                (v1, v2) -> v1 != null
                            && StringUtils.isNotEmpty(v1.toString()) 
                         ? v1 
                         : v2
                ));
  }

Пример тестового кода:

  public static void main(String[] args) {
    Map<String, Object> map1 = new HashMap<>();
    map1.put("k1", null);
    map1.put("k2", "2");
    map1.put("k3", null);
    Map<String, Object> map2 = new HashMap<>();
    map2.put("k1", "1");
    map2.put("k2", null);
    map2.put("k3", null);

    var result = mergeMaps(map1, map2);

    System.out.println(result);
  }

должно вывести что-то вроде {k1=1, k2=2, k3=null}; k1 был заменен на карту2, k2 остался из карты1, а k3 был null в обоих.

Большое спасибо.

Используйте Map<String, Optional<Object>>

daniu 29.05.2024 10:01

Почему вы не можете просто иметь дело с нулями? (v1, v2) -> (v1 == null || v1.toString().isEmpty()) ? v2 : v1

k314159 29.05.2024 10:27

Потому что Map.Entry::getValue вернуть NPE

Bourg 29.05.2024 10:59

@daniu это невозможно без использования Необязательно?

Bourg 29.05.2024 11:03

Где в документации сказано, что Map.Entry::getValue выбрасывает NPE? Эта функция сама по себе, конечно, не выбрасывает: она просто возвращает null, если значение равно null. Функция toMap выдает NPE, только если вы передаете null вместо Map.Entry::getValue. Неважно, вернется ли getValue()null.

k314159 29.05.2024 11:13

@ k314159 Проблема в том, что Map#merge не может обрабатывать записи со значением null, которое используется в сборщике toMap.

daniu 29.05.2024 11:23

@daniu Это было бы неправильное использование Optional. Я рекомендую против этого.

Anonymous 29.05.2024 13:55

Ваш код не компилируется. Пожалуйста, удалите ненужную точку с запятой. Кроме этого, если ваш код выдает NPE, разве это не из v1.toString(), поскольку v1 может быть null согласно тому, что вы говорите? Объясните, пожалуйста, для чего вообще нужен StringUtils.isNotEmpty()? Без этой информации я не могу понять ваши требования?

Anonymous 30.05.2024 01:38

@Bourg Я чувствую, что отрицательные голоса и придирчивые комментарии граничат с троллингом, но в них есть смысл; потребовалось время, чтобы понять, о чем вы спрашиваете и в чем проблема. Для будущих вопросов вам, вероятно, следует добавить код с примером ввода, показывающим проблему (как я это сделал в своем ответе).

daniu 30.05.2024 10:57

@daniu Я не согласен с тем, что желание понять требования находится на грани придирок. Я проголосовал против и проголосовал за закрытие.

Anonymous 31.05.2024 06:39

@daniu Я надеялся, что помог ФП улучшить вопрос, чтобы будущие читатели могли его понять. Я не вижу особой ценности в том, чтобы держать неясный вопрос при себе.

Anonymous 31.05.2024 09:36

@Anonymous Да, как я уже сказал, твои точки зрения верны. Тем не менее, я думаю, что лучший подход — просто предоставить правильный пример: у вас есть права на редактирование, вы можете просто удалить лишнюю точку с запятой самостоятельно, это занимает меньше времени, чем указание OP сделать это. Кроме того, я уже удалил упомянутый вами NPE, так что этот пункт больше не актуален, как и сопровождающее его закрытое голосование. Оставляет отсутствующие входные данные теста, что является лучшим моментом, но доступно в моем ответе; Я могу перенести это на вопрос, но не понимаю, как это делает пост в целом недействительным.

daniu 31.05.2024 09:45

Бург, лучшее, что вы могли бы сделать для пользователей Stack Overflow, — это по-прежнему отредактировать свой вопрос и полностью прояснить, как вы хотите расставить приоритеты (а) нулевых значений (б) пустых строк (в) объектов, возвращающих пустую строку из своего метода toString и (d) другие объекты (обычные объекты), происходящие из значений двух карт. Бонусы для вас включают в себя: существующий ответ может быть улучшен, я отзову свой отрицательный голос и проголосую за повторное открытие, что в конечном итоге может позволить вам получить больше ответов.

Anonymous 31.05.2024 23:05

@daniu Спасибо за улучшение вопроса. Он явно улучшился, но все еще неясен, и у меня нет информации, необходимой для разъяснения, поэтому я не могу ничего сделать дальше, извините.

Anonymous 02.06.2024 08:45
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
14
133
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

я бы пошел на

  Map<String, Object> mergeMaps(Map<String, Object> map1,
                                Map<String, Object> map2) {
    Map<String, Object> result = new HashMap<>(map1);
    map2.forEach((k, v) -> {
      var value = result.get(k);
      if (value == null || value.toString().isEmpty()) {
        result.put(k, v);
      }
    });
    return result;
  }

Что отлично работает для тестового примера.

Если map1 имеет запись "k1": "" (значение представляет собой нулевую строку), то ОП хочет, чтобы объединенная карта содержала значение из map2. Ваше решение этого не делает.

k314159 29.05.2024 11:31

@ k314159 да, тривиально, скорректировано.

daniu 29.05.2024 11:33

Теперь это хороший ответ. Хотя я бы поменял result.put(map2.get(k)); на result.put(v);.

k314159 29.05.2024 11:36

Спасибо @daniu и k314159 за вашу помощь. Это хорошо для меня.

Bourg 29.05.2024 11:58

два голоса "за" и принято, но даже не компилируется :- / --- 1) отсутствует );; 2) put() нужны два аргумента; 3) отсутствует isNullOrEmpty(Object)

user85421 29.05.2024 13:36

@ k314159 Если в map1 есть запись "k1": "" (…), то ОП хочет, чтобы объединенная карта содержала значение из map2. Нет, нигде в ОП этого не сказано.

Anonymous 30.05.2024 01:43

@Аноним, да, они это сделали: StringUtils.isNotEmpty(v1.toString()) ? v1 : v2

k314159 30.05.2024 10:49

@ k314159 Они этого не сделали. Они говорили о нулях. Вызов, который вы цитируете, завершится неудачно с NullPointerException, v1 имеет значение null, поэтому он никогда не работал, и то, что ОП намеревался с ним сделать, зависит от интерпретации.

Anonymous 31.05.2024 06:33

Другие вопросы по теме