Список фильтрации со значениями, допускающими значение NULL

Я пытаюсь заменить существующий цикл лямбда-выражениями Java.

У меня есть простая структура данных, которая выглядит следующим образом:

class DbObject{
    private Long objectId;

    private Long basedOnObjectId; //<- this field can be null

    public Long getObjectId() {
        return objectId;
    }

    public void setObjectId(Long objectId) {
        this.objectId = objectId;
    }

    public Long getBasedOnObjectId() {
        return basedOnObjectId;
    }

    public void setBasedOnObjectId(Long basedOnObjectId) {
        this.basedOnObjectId = basedOnObjectId;
    }
}

Затем у меня есть список DbObjects

И я пытаюсь отфильтровать каждый DbObject с указанным на основеOnObjectId:

(1) list.stream().filter(object -> obejct.getBasedOnObjectId()
             .equals(basedOnObject.getObjectId()))
             .collect(Collector.of(List))

Конечно, этот код дает мне NullPointer, потому что некоторые od DbObject не имеют BasedOnObjectId, потому что они являются корнями.

Поэтому естественно заменить поле «Long basedOnObjectId;» по желанию, но, как я уже упоминал в начале, существует существующий производственный код, потому что это не быстрое решение.

Итак, я пытаюсь внести некоторые изменения, я оцениваю следующее предложение:

 Optional<Long> option = Optional.ofNullable(objectWithNullBasedOnId.getBasedOnObjectId());

И попробуйте с методом .get() или .ifPresent(), но .get() также выдает исключение нулевого указателя, а ifPresent не предназначен для работы с потоком.

Я заметил это и тот факт, что когда у меня есть DbObject с нулем на основеOnObjectId, мне не нужно проверять, является ли значение точно нулевым, а только пропустить этот шаг выполнения.

Поэтому я решил использовать метод .orElse() и вернуть какое-то поддельное значение. В этом случае я возвращаю значение 0L, потому что этого индекса нет в БД :)

Итак, мой код:

 (2) List newList = list.stream().filter(object -> Optional.ofNullable(object.getBasedOnObjectId())
                             .orElse(0L)
                             .equals(basedOnObject.getObjectId()) )
                             .collect(Collectors.toList()) ;

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

Мой вопрос: если существует возможность достичь цели выражения (2) с использованием специального метода (в примере, необязательного) из java 8 и написать «в стиле потока» :)


person Mazeryt    schedule 22.06.2014    source источник
comment
Вы делаете это более сложным, чем это должно быть. Отфильтруйте нули: list.stream().filter(o -> o.basedOnObjectId() != null)...   -  person Brian Goetz    schedule 22.06.2014
comment
Или просто используйте Objects.equal, который обрабатывает значение null за вас.   -  person Brian Goetz    schedule 22.06.2014


Ответы (3)


Если вы сравниваете Optional<Long> вместо Long, нет необходимости различать присутствующие и отсутствующие объекты.

Optional<Long> root = Optional.of(basedOnObject.getObjectId());
list.stream()
    .map(DbObject::getBasedOnObjectId)
    .map(Optional::ofNullable)
    .filter(root::equals)
    .collect(Collectors.toList());

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

list.stream()
    .filter(o -> basedOnObject.getObjectId().equals(o.getBasedOnObjectId()))
    .collect(Collectors.toList());
person nosid    schedule 22.06.2014
comment
Спасибо, я упустил тот факт, что я могу изменить сравнение :) Но в другом случае .map(Optional::ofNullable) может быть хорошим решением - person Mazeryt; 22.06.2014

Вам не нужно представлять здесь Optional. Вам просто нужно знать, что null находится в вашем домене, и действовать соответственно. Как это:

list.stream().filter(o -> Objects.equals(o.getBasedOnObjectId(), 
                                         basedOnObject.getObjectId()))
             .collect(toList());

или путем явного отфильтровывания пустых значений (.filter(o -> o.getBasedOnObjectId() != null) или путем проверки нулевого значения в существующем условии фильтра.

Нет причин делать это более сложным, чем это необходимо. Objects.equals(), скорее всего, то, что вам нужно, просто используйте это.

person Brian Goetz    schedule 22.06.2014

Вероятно, вы захотите удалить нулевые объекты из потока, прежде чем передавать их последующим операциям. Попробуйте сопоставить объекты с формой BasedOnObjectId, а затем отфильтровать нулевые значения:

list.stream()
    .map(object -> object.getBasedOnObjectId())
    .filter(object -> object != null)
    ...
person Matthew Franglen    schedule 22.06.2014