【问题标题】:"Partly" reducing a Stream“部分”减少流
【发布时间】:2020-11-14 20:29:35
【问题描述】:

鉴于以下上下文:

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。

我相信我可以使用一些帮助 :-)

【问题讨论】:

  • 你的collect 方法中的I 是什么?这是使用泛型还是您输入了错误的接口/类的名称?
  • 将其作为实现。应该使用'A'
  • 在这种情况下您可以edit your question 相应地更新代码。
  • 另外,您是否在寻找类似public Set&lt;A&gt; collect(Stream&lt;A&gt; stream) { return stream .filter(A::canCrunch) .map(a -&gt; a.crunch(a)) .collect(Collectors.toSet()); } 的东西?
  • 不完全是。想想商店物品。相同的产品可以“融合”成具有更高数量属性的单一身份产品,但仅限于其盒子大小。 a.canCrunch 说明了参与紧缩的一般能力(并且打电话更便宜)。 a.canCrunch(a2) 检查这两个候选人是否真的有效配对。

标签: java java-stream reduce


【解决方案1】:

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

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();
}

这两种方法都应该产生所需的输出。他们获取一个包含任意Additives 的Stream,按实际类型对它们进行分组,然后将相同类型的那些压缩成一个对象,最后返回一个SetCollection,每个@ 类型只包含一个对象987654325@.

我列出了两种不同的方法。第一种方法首先组合成一个地图,然后处理所有相似的类型。这也许是更容易理解的方法。

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

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

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

【讨论】:

  • 精彩的回答,谢谢。我从未见过.groupBy 方法。我现在肯定会考虑更多。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-15
  • 1970-01-01
  • 2019-02-09
  • 1970-01-01
  • 2018-04-24
  • 2015-11-05
  • 1970-01-01
相关资源
最近更新 更多