【问题标题】:Java 8 multiple mappingJava 8 多重映射
【发布时间】:2016-03-18 23:00:16
【问题描述】:

是否可以对集合执行多个映射? 以下代码编译错误:

...在 Stream 中不能应用到java.util.function.Function<capture<?>,capture<?>>

private static List<?> multipleMapping(final Collection<?> collection, final List<Function<?, ?>> functions) {
    Stream<?> stream = collection.stream();
    for (Function<?, ?> function : functions) {
        stream = stream.map(function);
    }
    return stream.collect(Collectors.toList());
}

我想要通用解决方案。

【问题讨论】:

  • Functions 是否都返回与输入相同的类型,还是返回不同的类型?
  • 你最多有多少个函数?
  • 最后一个函数返回的类型是什么?
  • 可能在任何情况下都是 Comparable
  • @Artur 那么函数的最大数量呢?我们说的是一对、3 个、4 个或 5 个,还是几十个?

标签: java java-8 functional-interface


【解决方案1】:

问题在于您使用的是通用通配符?。您想要的是有一个参数化类型T,它将代表 Stream 元素的类型。假设该函数将返回与其输入相同的类型,您可以:

private static <T> List<T> multipleMapping(final Collection<T> collection, final List<Function<T, T>> functions) {
    Stream<T> stream = collection.stream();
    for (Function<T, T> function : functions) {
        stream = stream.map(function);
    }
    return stream.collect(Collectors.toList());
}

这编译得很好:给map 的映射器正确地接受T 并返回T。但是,如果函数返回的类型与其输入不同,那么您将无法保持类型安全,而不得不求助于使用 List&lt;Function&lt;Object, Object&gt;&gt;


请注意,我们可以使用 UnaryOperator&lt;T&gt; 代替 Function&lt;T, T&gt;

此外,您可以避免 for 循环并使用 andThen 将所有函数简化为一个函数:

private static <T> List<T> multipleMapping(final Collection<T> collection, final List<Function<T, T>> functions) {
    return collection.stream()
                     .map(functions.stream().reduce(Function.identity(), Function::andThen))
                     .collect(Collectors.toList());
}

【讨论】:

  • 是的,没关系。但我有不同的功能,例如:A -> B -> C -> D - 不是 T -> T
  • @akfmb 那么你不能有类型安全。您可以拥有的最好的方法是List&lt;Function&lt;Object, Object&gt;&gt;,如果您希望尽可能通用。如果你想要类型安全,你可以做的是有多个重载方法multipleMapping(List&lt;T&gt;, Function&lt;T, R1&gt;, Function&lt;R1, R&gt;等......
  • 你应该在流之外组合函数,所以你不要为集合的每个元素组合它们
  • 我的函数列表有时有 1 个元素 - 有时是 2,3,4 - 元素的数量不是恒定的。所以我认为(正如@Tunaki 所说)对我来说最好的选择是使用 Object。
【解决方案2】:

如果您的功能很少(即如果您可以将它们写下来),那么我建议您不要将它们添加到列表中。相反,将它们组合成一个函数,然后将该单个函数应用于给定集合的每个元素。

您的 multipleMapping() 方法现在将接收一个函数:

public static <T, R> List<R> multipleMapping(
    Collection<T> collection, Function<T, R> function) {

    return collection.stream()
            .map(function)
            .collect(Collectors.toList());
}

然后,在调用代码中,您可以创建一个由许多函数组成的函数(无论如何,您将拥有所有函数)并使用该函数调用 multipleMapping() 方法。

例如,假设我们有一个候选人列表:

List<String> candidates = Arrays.asList(
        "Hillary", "Donald",
        "Bernie", "Ted", "John");

还有四个功能:

Function<String, Integer> f1 = String::length;

Function<Integer, Long> f2 = i -> i * 10_000L;

Function<Long, LocalDate> f3 = LocalDate::ofEpochDay;

Function<LocalDate, Integer> f4 = LocalDate::getYear;

这些函数可以用来组成一个新函数,如下:

Function<String, Integer> function = f1.andThen(f2).andThen(f3).andThen(f4);

也可以这样:

Function<String, Integer> composed = f4.compose(f3).compose(f2).compose(f1);

现在,您可以使用候选列表和组合的 function 调用您的 multipleMapping() 方法:

List<Integer> scores = multipleMapping(candidates, function);

因此,我们通过从四个不同的函数显式组合一个新函数并将这个组合函数应用于每个候选函数,将候选列表转换为分数列表。

If you want to know who will win the election, you could check which candidate has the highest score, but I will let that as an exercise for whoever is interested in politics ;)

【讨论】:

  • 组合功能看起来更好,并且保持了类型控制。谢谢@Federico。您的解决方案解决了我的问题。
猜你喜欢
  • 2018-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
相关资源
最近更新 更多