我是否需要自定义 Spliterator 以避免额外的 .stream() 调用?

2022-01-22 00:00:00 java-8 java java-stream

我有这段代码可以正常工作,但我觉得它很难看.

I have this code which works fine, but I find it ugly.

@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 列表.

ActionsBloc is a super type which contains a list of Action.

public interface ActionsBloc {

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

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

我要做的是根据 Class 类型将 Actionsblocks 合并在一起.所以我按 ActionsBloc::getClass 分组,然后通过调用 ActionsBloc::mergeWith 进行合并.

What I want to do is merge blocs of Actions together based on the Class type. So I'm grouping by ActionsBloc::getClass and then merge by calling ActionsBloc::mergeWith.

我觉得丑陋的是在 collect 上结束第一个流之后调用 values().stream().

What I find ugly is calling the values().stream() after the first stream was ended on collect.

有没有办法只对一个流进行操作并摆脱 values().stream(),还是我必须编写一个自定义 Spliterator?换句话说,我的代码中只有一个 collect.

Is there a way to operate only on one stream and get rid of values().stream(), or do I have to write a custom Spliterator? In other words have only one collect in my code.

推荐答案

您可以使用减少标识来解决这个问题.一种方法是将 mergeWith 的实现更新为:

You can work with a reducing identity to sort that out possibly. One way could be to update the implementation of mergeWith as :

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

然后将groupingreduction修改为:

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

Edit:正如 Holger 指出的那样,使用 groupingByreducing 的用例可以更合适地使用 toMap 为:

Edit: As Holger pointed out such use cases of using groupingBy and reducing further could be more appropriately implemented using toMap as :

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

相关文章