【问题标题】:How do I add map and filter when I extend ArrayList in Java?在 Java 中扩展 ArrayList 时如何添加映射和过滤器?
【发布时间】:2018-08-26 16:46:18
【问题描述】:

我正在尝试创建一个扩展 ArrayListtable 类。在其中,我希望能够创建一个 map 方法,该方法采用 lambda 表达式并返回一个带有映射值的新 table。我也想用filter 来做这件事。我经常使用地图和过滤器,我不喜欢一遍又一遍地输入整个内容。

public abstract class Table<E extends Element> extends ArrayList<E> {
    // a lot of other stuff.

    public Table<E> map(/*WHAT DO I PUT HERE?*/ mapper) {
        return this.stream().map(mapper).collect(/*WHAT DO I PUT HERE?*/);
    }

    public Table<E> filter(/*WHAT DO I PUT HERE?*/ predicate) {
        return this.stream().filter(predicate).collect(/*WHAT DO I PUT HERE?*/);
    }
}

我仍在尝试找出泛型。也许有更好的方法。我不知道。我尝试复制 ArrayList 的原始代码中的内容,但我尝试的一切似乎都会产生新问题。

【问题讨论】:

  • Table 是一个抽象类,你会怎么收集到它里面?
  • 在你的地图功能中,你想映射到什么?据我了解,源和结果是 E 流
  • @Andrew 你同样可以问:List 是一个接口,Collectors.toList 怎么收集进去?
  • 请参考 Efficient Java 为什么扩展ArrayList 不是一个好主意。
  • 不要扩展,组合。写一个class Table implements List 并在后台使用ArrayList

标签: java generics arraylist java-8 extends


【解决方案1】:

一方面,这是完全可能的:

public abstract class Table<E extends Element> extends ArrayList<E> {
    // implement in concrete subclasses
    public abstract <E1 extends Element> Collector<E1, ?, Table<E1>> collector();
    // Collector<E, ?, Table<E>> collector() is enough if map doesn't have E1 type parameter

    public <E1 extends Element> Table<E1> map(Function<E, E1> mapper) {
        return this.stream().map(mapper).collect(collector());
    }

    public Table<E> filter(Predicate<E> predicate) {
        return this.stream().filter(predicate).collect(collector());
    }
}

public class ChildTable<E extends Element> extends Table<E> {
    @Override
    public <E1 extends Element> Collector<E1, ?, Table<E1>> collector() {
        return Collectors.toCollection(() -> new ChildTable<E1>());
        // or simpler Collectors.toCollection(ChildTable::new);
    }
}

collector() 可以实现,但它必须返回Table 的特定子类型。

另一方面,最好将列表作为Table字段,而不是扩展它:更喜欢组合而不是继承。您真的希望所有ArrayList 方法都可用吗?

【讨论】:

  • 我喜欢这个想法,它看起来像模板方法模式
  • 是的!模板模式是非常好的方法。
  • 在子表中,我将如何实现collector()
  • @MakPo 例如Collector.toCollection(() -&gt; new TableSubclass&lt;E1&gt;()).
  • 因为只有供应商不同,所以如果只声明abstract &lt;E1&gt; Supplier&lt;Table&lt;E1&gt;&gt; tableSupplier()方法而不是collector(),由子类实现,设计会更简单。
【解决方案2】:

只需将Function&lt;E, E&gt; 用于映射器,因为您的返回类型是相同的对象,而Predicate&lt;E&gt; 用于谓词,因为E 将被谓词。 对于 Table,无论您在 collect 中使用什么实现类。查看收集的工作方式。

它基本上需要一个初始化器 (first argument),BiFunction 指定如何将元素添加到集合中 (second argument),最后 BiFunction 指定它在并行流的情况下如何工作 (third argument)。请看下面的代码:-

public abstract class Table<E extends Element> extends ArrayList<E> {
    public Table<E> map(Function<E, E> mapper) {
        return this.stream().map(mapper).collect(TableImpl::new, TableImpl::add, TableImpl::addAll);
    }

    public Table<E> filter(Predicate<E> predicate) {
        return this.stream().filter(predicate).collect(TableImpl::new, TableImpl::add, TableImpl::addAll);
    }
}

class TableImpl extends Table {
//whatever you impl is
}

请随时将收集实现提取到Collector,以避免重复代码。

【讨论】:

  • 或使用UnaryOperator&lt;E&gt; 代替Function&lt;E, E&gt;
  • 不!这是一个通用的实现。 UnaryOperator 将返回相同的对象,而 OP 期望映射器的通用 impl。但是如果操作是一元的,那么它可以作为参数传递,同时调用mapfilter 作为UnaryOperator 扩展Function。不过你问得很好。
  • 您确实意识到Function&lt;E, E&gt; 接受E 并返回E,这正是UnaryOperator&lt;E&gt; 所做的吗? UnaryOperator 将返回对象的 same type 而不是 same object 这也是 Function&lt;E, E&gt; 所做的
  • 我不明白 TableImpl 类应该是什么或我需要在其中放入什么。
  • 这只是您需要在自定义收集器中编写的示例。您可以将其提取到方法或方法的参数中,就像方法中的另一个参数一样。我建议使用一个参数来传递要使用的收集器 impl。
猜你喜欢
  • 2021-09-13
  • 2017-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-26
  • 1970-01-01
  • 1970-01-01
  • 2019-09-04
相关资源
最近更新 更多