Java 8 Streams-сбор значений, которые могут быть null

у меня есть следующий код:

    private static <T> Map<String, ?> getDifference(final T a, final T b, final Map<String, Function<T, Object>> fields) {
    return fields.entrySet().stream()
            .map(e -> {
                final String name = e.getKey();
                final Function<T, Object> getter = e.getValue();
                final Object pairKey = getter.apply(a);
                final Object pairValue = getter.apply(b);
                if (Objects.equals(pairKey, pairValue)) {
                    return null;
                } else {
                    return Pair.of(name, pairValue);
                }
            })
            .filter(Objects::nonNull)
            .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
    }

Теперь pairValue может быть null. Чтобы избежать NPE, как описано здесь, в то время как"сбор" ing, я хочу убедиться, что я отправляю только те значения, которые не являются нулевыми. Если null, я хочу отправить "".

Итак, я попытался заменить последнюю строку с этой:

.collect(Collectors.toMap(Pair::getKey,Optional.ofNullable(Pair::getValue).orElse(""));

и другие его модификации:

.collect(Collectors.toMap(pair -> pair.getKey(), Optional.ofNullable(pair -> pair.getValue()).orElse(""));

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

2 ответов


у вас неправильный синтаксис. toMap()второй параметр должен быть лямбда, поэтому

.collect(Collectors.toMap(
             pair -> pair.getKey(),
             pair -> Optional.ofNullable(pair.getValue()).orElse("")
));

или

вы можете изменить следующим образом

return Pair.of(name, Optional.ofNullable(pairValue).orElse(""));

и используйте свой оригинальный collect()


вы можете просто собирать в HashMap позволяет null значения без необходимости Optional:

private static <T> Map<String, Object> getDifference(
        final T a, final T b, final Map<String, Function<T, Object>> fields) {
    return fields.entrySet().stream()
        .map(e -> {
            final Function<T, Object> getter = e.getValue();
            final Object value = getter.apply(b);
            return Objects.equals(getter.apply(a),value)? null: Pair.of(e.getKey(), value);
        })
        .filter(Objects::nonNull)
        .collect(HashMap::new, (m,p) -> m.put(p.getKey(),p.getValue()), Map::putAll);
}

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

для сравнения, здесь та же операция без потока:

private static <T> Map<String, Object> getDifference(
        final T a, final T b, final Map<String, Function<T, Object>> fields) {
    HashMap<String, Object> result = new HashMap<>();
    fields.forEach((key, getter) -> {
        final Object value = getter.apply(b);
        if(!Objects.equals(getter.apply(a), value)) result.put(key, value);
    });
    return result;
}

конечно, это также будет работать с необязательными:

private static <T> Map<String, Optional<Object>> getDifference(
        final T a, final T b, final Map<String, Function<T, Object>> fields) {
    HashMap<String, Optional<Object>> result = new HashMap<>();
    fields.forEach((key, getter) -> {
        final Object value = getter.apply(b);
        if(!Objects.equals(getter.apply(a), value))
            result.put(key, Optional.ofNullable(value));
    });
    return result;
}

но если все вы хотите сделать, это заменить null С пустой строкой, вам не нужно Optional:

private static <T> Map<String, Object> getDifference(
        final T a, final T b, final Map<String, Function<T, Object>> fields) {
    HashMap<String, Object> result = new HashMap<>();
    fields.forEach((key,getter) -> {
            final Object value = getter.apply(b);
            if(!Objects.equals(getter.apply(a), value))
                result.put(key, value==null? "": value);
        });
    return result;
}

и хорошо, эта замена также будет работать из коробки с вашим исходным кодом, если вы просто сделаете это в map функция вместо коллектора:

private static <T> Map<String, ?> getDifference(final T a, final T b, final Map<String, Function<T, Object>> fields) {
    return fields.entrySet().stream()
        .map(e -> {
            final String name = e.getKey();
            final Function<T, Object> getter = e.getValue();
            final Object pairKey = getter.apply(a);
            final Object pairValue = getter.apply(b);
            if (Objects.equals(pairKey, pairValue)) {
                return null;
            } else {
                return Pair.of(name, pairValue==null? "": pairValue);
            }
        })
        .filter(Objects::nonNull)
        .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}

или

private static <T> Map<String, Object> getDifference(
        final T a, final T b, final Map<String, Function<T, Object>> fields) {
    return fields.entrySet().stream()
        .map(e -> {
            final Function<T, Object> getter = e.getValue();
            final Object pairValue = getter.apply(b);
            return Objects.equals(getter.apply(a), pairValue)? null:
                Pair.of(e.getKey(), pairValue==null? "": pairValue);
        })
        .filter(Objects::nonNull)
        .collect(Collectors.toMap(Pair::getKey, Pair::getValue));
}