"Partly" 减少流

"Partly" reducing a Stream

给定以下上下文:

public interface IAdditive<T> {
    /** True if still capable to crunch. */
    boolean canCrunch();
    /** True if capable to crunch with B. */
    boolean canCrunch(T other);
    /** Returns a new T which is the sum of this and other */
    T crunch(T other);    
}

class A implements IAdditive<A> {
    ..
    A crunch(A other) {...}
}

class B extends A {
    ...
    B crunch(B other) {...}
}

class C implements IAdditive<C> {
    ...
    C crunch(C other) {...}
}

现在我想“处理”一个实施流

/** Chrunches the streams where possible */
public Stream<A> crunchStream(Stream s) {
    return s.map(...);
}

我坚持我相当幼稚的方法:

public Set<A> collect(Stream<A> stream) {
    Set<I> res = new HashSet<>();
    Set<I> set = stream
            .filter(IAdditive::canCrunch)
            .collect(Collectors.toSet());
    set.forEach(setItem -> set.stream()
            .filter(concurrentItem -> concurrentItem.canCrunch(setItem))
            .map(setItem::crunch)
            .forEach(res::add));
    return res;
}

这应该是有缺陷的。我正在展开流,添加强制性复杂性,如果我希望界面以默认方法提供它,我将不得不使用 rawtypes。

我相信我需要一些帮助:-)

根据您的评论,我认为这就是您想要的:

public static interface Additive<T> {
    default public Additive<T> crunch(Additive<T> other) { return null; }
}

public static class A implements Additive<A> {};

public static class B implements Additive<B> {};

public static class C implements Additive<C> {};

/**
 * Takes a stream of arbitrary Additives and returns a Set containing
 * only one crunched Additive for each type
 * 
 * @param stream additives to crunch
 * @return crunched set
 */
public Set<Additive<?>> crunch(Stream<Additive<?>> stream) {
    return stream
        .collect(Collectors.groupingBy(o -> o.getClass()))
        .values().stream()
        .map(values -> values.stream().reduce(Additive::crunch).get())
        .collect(Collectors.toSet());
}

/**
 * Takes a stream of arbitrary Additives and returns a Set containing
 * only one crunched Additive for each type
 * 
 * @param stream additives to crunch
 * @return crunched set
 */
public Collection<Additive<Object>> crunchMap(Stream<Additive<?>> stream) {
    return stream
        .collect(Collectors.toMap(k -> k.getClass(), v -> (Additive<Object>) v, (a, b) -> a.crunch(b))).values();
}

两种方法都应该产生所需的输出。他们采用包含任意 Additive 的 Stream,将它们按实际类型分组,然后将相同类型的那些压缩成一个对象,最后 return 一个 SetCollection 每种 Additive.

只包含一个对象

我列出了两种不同的方法。第一种方法首先分组到地图中,然后处理所有相似的类型。这可能是更容易理解的方法。

第二种方法使用映射收集器,将每个附加值映射到它的 class 作为键,并对值执行空操作,然后在键冲突时,将新值与旧值进行处理值并将其放入地图中。它涉及更多,更难阅读,需要更通用的功能才能真正开始工作。

请注意,将 Stream 作为参数传递没有任何好处 - 只需传递集合。事实上,不鼓励传递流,因为您永远无法确定谁已经对流进行了操作,而谁还没有,这会在流已经关闭时导致异常。

请注意,我没有使用您的 canCrunch 方法,而是依赖于每个类型都可以自我压缩并且只能自我压缩这一事实,但不少于此。这比不能压碎的相同类型的对象更容易执行和处理。如果需要,您需要某种形式来区分它们并相应地更改 classificator。