【问题标题】:Helper methods for creating tuples and tupled functions in Java?在 Java 中创建元组和元组函数的辅助方法?
【发布时间】:2016-02-12 00:18:53
【问题描述】:

我正在使用 Java 8 以及来自 Apache Commons Lang3 的 Pair。

我要做的第一件事是从List<T> 获取流并获取Function<T,U> 并最终创建List<Pair<T,U>>。目前我正在使用我想要的特定类型创建一个Function<T,Pair<T,U>>,并使用它来映射流。我想要的是这样的:

public static <T, U> Function<T, Pair<T, U>> tupledResult(final Function<T, U> generator) {
    Objects.requireNonNull(generator);
    return (T t) -> new ImmutablePair<>(t, generator.apply(t));
}

下一个问题是,现在我有了List&lt;Pair&lt;T, U&gt;&gt;,我希望能够使用foreach 来应用BiConsumer&lt;T,U&gt;(类似于Scala 中的tupled 函数)。我猜它看起来像:

public static <T, U> Consumer<Pair<T, U>> tupled(final BiConsumer<T, U> consumer) {
    Objects.requireNonNull(consumer);
    return (final Pair<T, U> p) -> consumer.accept(p.getLeft(), p.getRight());
}

Apache Commons Lang3 中有什么可以做到这一点还是我应该自己推出?如果是后者,这对贡献有用还是不好的解决方案?

示例

这是我想要实现的目标:

private void doStuff(final List<Thing> things) {
    final List<Pair<Thing, Other>> otherThings = things.stream()
        .map(tupledResult(ThingHelper::generateOther))
        .collect(Collectors.toList());
    otherThings.stream().forEach(tupled((final Thing thing, final Other other) -> {
        doSomething(thing, other);
    }));
    otherThings.stream().forEach(tupled((final Thing thing, final Other other) -> {
        doSomethingElse(thing, other);
    }));
}

这里的要点是ThingHelper.generateOther比较贵,我只想做一次。此外,doSomething 必须先应用于所有内容,然后再应用于 doSomethingElse

对永远不会离开此方法的范围,我也不想重载方法来获取对。在这种情况下,我真的不在乎这对缺乏语义,重要的是顺序。 doSomethingdoSomethingElse 是提供语义的。

【问题讨论】:

  • 有趣的是java有很多Bi-类型;但它坚决拒绝Pair
  • @bayou.io:这些Bi- 类型是无状态函数,与Pair 类型不同,后者是一个存储。它的添加已被延迟,因为他们希望等待拥有真值类型,但 Optional 也已添加,它是一种值类型,但在 JVM 中还没有真值类型。除此之外,还有 一个 pair 类型,如果你能接受它的不切实际的名字 AbstractMap.SimpleImmutableEntry...
  • @Steiny:你不应该需要这种冗长的 lambda 语法 (final Thing thing, final Other other) -&gt; { doSomething(thing, other); } 一个简单的 (thing, other) -&gt; doSomething(thing, other) 应该也可以。或者方法参考……

标签: java java-8 tuples apache-commons-lang3


【解决方案1】:

在 FunctionalJava (https://github.com/functionaljava/functionaljava) 中我会这样做:



    private void doStuff(final List<Thing> things) {
        fj.data.List<P2<Thing, Other>> otherThings = fj.data.List.list(things)
            .map(t -> P.p(t, ThingHelper.generateOther.apply(t)));
        otherThings.forEachDoEffect(p -> doSomething(p._1(), p._2()));
        otherThings.forEachDoEffect(p -> doSomethingElse(p._1(), p._2()));
    }

元组作为 P、P1、P2 等类的产品受支持 (https://functionaljava.ci.cloudbees.com/job/master/javadoc/)。

【讨论】:

    【解决方案2】:

    Apache Commons Lang3 中不存在此类方法,因为该库与 Java 6 兼容,但您想要的方法必须返回仅在 Java 8 中出现的 java.util.function 包的对象。

    如果您的 Thing 对象没有重复,那么在您的情况下使用 Map 代替是很自然的:

    private void doStuff(final List<Thing> things) {
        final Map<Thing, Other> otherThings = things.stream()
            .collect(Collectors.toMap(Function.identity(), ThingHelper::generateOther));
        otherThings.forEach((final Thing thing, final Other other) -> {
            doSomething(thing, other);
        });
        otherThings.forEach((final Thing thing, final Other other) -> {
            doSomethingElse(thing, other);
        });
    }
    

    甚至更短:

    private void doStuff(List<Thing> things) {
        Map<Thing, Other> otherThings = things.stream()
            .collect(toMap(x -> x, ThingHelper::generateOther));
        otherThings.forEach(this::doSomething);
        otherThings.forEach(this::doSomethingElse);
    }
    

    这样你就不需要包装器了,因为Map.forEach 已经接受了BiConsumerCollectors.toMap 第二个参数基本上取代了你的tupledResult

    【讨论】:

    • 可以去掉final修饰符,让代码更加简洁。
    • 这要好得多。我喜欢。不知道为什么我要避免使用地图,因为这些事情不会重复。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-12
    • 2010-12-24
    • 2015-05-27
    • 2023-01-10
    • 2020-10-21
    相关资源
    最近更新 更多