Нужен ли мне собственный Spliterator, чтобы избежать дополнительного вызова .stream ()? - PullRequest
2 голосов
/ 20 июня 2019

У меня есть этот код, который отлично работает, но я нахожу его уродливым.

@EqualsAndHashCode
public abstract class Actions {

    @Getter
    private List<ActionsBloc> blocs;

    public Actions mergeWith(@NotNull Actions other) {

        this.blocs = Stream.of(this.blocs, other.blocs)
                                    .flatMap(Collection::stream)
                                    .collect(groupingBy(ActionsBloc::getClass, reducing(ActionsBloc::mergeWith)))
                                    .values()
                                    .stream()
                                    .filter(Optional::isPresent)
                                    .map(Optional::get)
                                    .collect(toList());

        return this;
    }
}

ActionsBloc - это супертип, который содержит список Action.

public interface ActionsBloc {

    <T extends Action> List<T> actions();

    default ActionsBloc mergeWith(ActionsBloc ab) {
        this.actions().addAll(ab.actions());
        return this;
    }
}

Что я хочу сделать, это объединить blocs из Actions вместе на основе типа Class. Поэтому я группирую по ActionsBloc::getClass, а затем объединяюсь, вызывая ActionsBloc::mergeWith.

То, что я считаю уродливым, вызывает values().stream() после того, как первый поток закончился collect.

Есть ли способ работать только на одном потоке и избавиться от values().stream(), или мне нужно написать собственный Spliterator? Другими словами, в моем коде есть только один collect.

1 Ответ

4 голосов
/ 20 июня 2019

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

default ActionsBloc mergeWith(ActionsBloc ab) {
    this.actions().addAll(Optional.ofNullable(ab)
            .map(ActionsBloc::actions)
            .orElse(Collections.emptyList()));
    return this;
}

, а затем измените grouping и reduction на:

this.blocs = new ArrayList<>(Stream.of(this.blocs, other.blocs)
        .flatMap(Collection::stream)
        .collect(groupingBy(ActionsBloc::getClass, reducing(null, ActionsBloc::mergeWith)))
        .values());

Редактировать : Как отметил Хольгер, такие варианты использования groupingBy и reducing могут быть более целесообразно реализованы с использованием toMap как:

this.blocs = new ArrayList<>(Stream.concat(this.blocs.stream(), other.blocs.stream())
        .collect(Collectors.toMap(ActionsBloc::getClass, Function.identity(), ActionsBloc::mergeWith))
        .values());
...