Есть ли способ Stream Map Filter & Map обратно на оригинальный объект в Java 8? - PullRequest
4 голосов
/ 08 мая 2019

Есть ли способ для потоковой передачи списка -> карта -> фильтр -> отобразить исходный тип объекта списка?

Существует решение, если мы делаем это, используя foreach, как показано ниже:

List<Query> updatedQueries = getUpdatedQueries();

List<Query> finalQueries = new ArrayList<>();
updatedQueries.forEach(query -> {

    Period period = getPeriodRequest(query);
    boolean isValidPeriod = periodService.validatePeriodicity(period);
    if(isValidPeriod &&  isMandatory(period)){
        finalQueries.add(query);
    }

});

Но есть ли способ сделать это следующим образом?

List<Query> updatedQueries = getUpdatedQueries();

List<Query> finalQueries = updatedQueries
        .stream()
        .map(this::getPeriodRequest) //returns the object of type Period
        .filter(period->periodService.validatePeriodicity(period))
        .filter(this::isMandatory)
        //is any way we can map back to Query object (without any object translation  function)
        .collect(Collectors.toList());

Ответы [ 5 ]

7 голосов
/ 08 мая 2019

Попробуйте это

List<Query> finalQueries = updatedQueries
    .stream().filter(query->{
        Period period = getPeriodRequest(query);
        return periodService.validatePeriodicity(period )&& isMandatory(period))
    })
    .collect(Collectors.toList());
5 голосов
/ 08 мая 2019

Вы можете расширить filter как:

    List<Query> finalQueries = updatedQueries
            .stream()
            .filter(query -> {
                Period period = getPeriodRequest(query);
                return periodService.validatePeriodicity(period) && isMandatory(period);
            })
            .collect(Collectors.toList());
1 голос
/ 08 мая 2019

Прежде всего ваш фактический результат имеет тип List<Period> finalQueries из-за этого map(this::getPeriodRequest). Просто используйте несколько «длинных» лямбд:

 updatedQueries.stream()
               .filter(q -> periodService.validatePeriodicity(q.getPeriodRequest()))
               .filter(q -> isMandatory(q.getPeriodRequest()))
               .collect(Collectors.toList())

Вы даже можете сжать эти два filter в один и читать q.getPeriodRequest() только один раз, если вы действительно этого хотите.

Или вы можете сопоставить SimpleEntry, например:

 updatedQueries.stream()
               .map(x -> new SimpleEntry<>(x, x.getPeriodRequest()))
               .filter(e -> periodService.validatePeriodicity(e.getValue()))
               .filter(e -> isMandatory(e.getValue()))
               .map(Entry::getKey)
               .collect(Collectors.toList());
0 голосов
/ 08 мая 2019

Зачем вообще использовать Stream s?

updatedQueries.removeIf(query-> {
    Period period = getPeriodRequest(query);
    return !periodService.validatePeriodicity(period) || !isMandatory(period));
});

При этом удаляются все элементы из updatedQueries, которые не соответствуют фильтру.Ведет себя так же, но не вводит новый List.

. Наилучшим подходом, вероятно, будет использование простого for -цикла, если вы заботитесь о производительности:

for (Iterator<Query> it = updatedQueries.iterator(); it.hasNext(); ) {
    Period period = getPeriodRequest(it.next());
    if(!periodService.validatePeriodicity(period) || !isMandatory(period))) {
        it.remove();
    }
}
0 голосов
/ 08 мая 2019

Нет.Вы должны как-то хранить оригинальный объект.

Вот так

class Pair<A, B> {
    A a;
    B b;
    // constructors and getters skipped
}

Тогда вы можете сделать следующее:

list.stream()
   .map(x -> new Pair<>(x, getOtherObject()))
   .filter(x -> checkOther(x.getB()))
   .map(Pair::getA)
   .collect(Collectors.toList());
...