【问题标题】:adding all results from running a method on elements in a collection to another collection将在集合中的元素上运行方法的所有结果添加到另一个集合
【发布时间】:2012-12-23 11:03:12
【问题描述】:

有没有一种 lambdaj 方法可以巧妙地做到这一点?我知道标题听起来很复杂,但下面的代码片段应该清楚:

private List<String[]> getContractLineItemsForDatatables(List<ContractLineItem> contractLineItems) {

    List<String[]> contractLineItemsForDatatables = Lists.newArrayList();

    for (ContractLineItem contractLineItem : contractLineItems) {

        contractLineItemsForDatatables.add( contractLineItem.getDataTablesRow());
    }

    return contractLineItemsForDatatables;
}

必须有一种巧妙的方法来使用 Lambdaj 来避免上面的 for 循环,但我无法理解它。顺便说一句,contractLineItem.getDatatablesRow() 返回一个 String[]。

所以我想做的是:

对 contractLineItems 列表中的所有元素运行 getDataTablesRow(),并将它们添加到 contactLineItemsForDatatables 列表中。

有什么建议吗?

【问题讨论】:

  • 如果您的代码有效,为什么要“修复它”?
  • 除非您在 Java 8 中对 lambda 有适当的支持,否则在这种情况下使用 lambda 可能会更加复杂和耗时。 Lambda 将使用相同的循环,只是效率不高。 ;)
  • @Peter Lawrey 感谢这与效率无关(不是性能关键的应用程序),更多的是好奇 Lambdaj 版本是否可以更可读
  • 如果可读性意味着更少的符号,我对此表示怀疑。让我尝试将它添加到我的 asnwer 中(这将需要一些时间;)
  • @AshkanAryan Guava 有很多“功能性”方法可供您使用,但在他们的文档中,当人们在“传统方式”更具可读性的情况下使用它时,它们会“哭泣”。 .. 真的,你应该等待 Java 8 ;)

标签: java collections closures guava lambdaj


【解决方案1】:

大部分冗长来自变量名的选择,而不是 Java 代码。

private static List<String[]> extractDataTableRows(List<ContractLineItem> items) {
    List<String[]> ret = new ArrayList<>();
    for (ContractLineItem item : items) ret.add(item.getDataTablesRow());
    return ret;
}

如果抛出异常,它看起来像

Exception in thread "main" java.lang.RuntimeException
at Main$ContractLineItem.getDataTablesRow(Main.java:87)
at Main.extractDataTableRows(Main.java:50)
at Main.main(Main.java:27)

使用 Guava 的 lambda

private static List<String[]> extractDataTableRows(List<ContractLineItem> items) {
    return Lists.transform(items, new Function<ContractLineItem, String[]>() {
        @Override
        public String[] apply(ContractLineItem item) {
            return item.getDataTablesRow();
        }
    });
}

如果抛出异常,它看起来像

Exception in thread "main" java.lang.RuntimeException
at Main$ContractLineItem.getDataTablesRow(Main.java:76)
at Main$1.apply(Main.java:38)
at Main$1.apply(Main.java:35)
at com.google.common.collect.Lists$TransformingRandomAccessList.get(Lists.java:495)
at java.util.AbstractList$Itr.next(AbstractList.java:358)
at java.util.AbstractCollection.toString(AbstractCollection.java:459)
at java.lang.String.valueOf(String.java:2957)
at java.io.PrintStream.println(PrintStream.java:821)
at Main.main(Main.java:31)

注意:在使用 List 之前不会触发异常。

这个Guava Caveat对我来说已经够清楚了。

从 Java 7 开始,Java 中的函数式编程只能通过笨拙而冗长地使用匿名类来近似。这在 Java 8 中预计会有所改变,但 Guava 目前针对的是 Java 5 及更高版本的用户。

过度使用 Guava 的函数式编程习语会导致代码冗长、混乱、不可读和效率低下。到目前为止,这些是 Guava 中最容易(也是最常见)被滥用的部分,而当您竭尽全力使您的代码“单行”时,Guava 团队会哭泣。

使用 Java 8。

private static List<String[]> extractDataTableRows(List<ContractLineItem> items) {
    return items.stream()
            .<String[]>map(ContractLineItem::getDataTablesRow)
            .into(new ArrayList<>());
}

添加一个你可以编写的实用方法

public static <E, R> List<R> map(Collection<E> elements, Function<? super E, ? extends R> function) {
    return elements.stream().<R>map(function).into(new ArrayList<R>());
}

// hiding the guff, this is more readable IMHO.
private static List<String[]> extractDataTableRows(List<ContractLineItem> items) {
    return map(items, ContractLineItem::getDataTablesRow);
}

如果抛出异常,它可能看起来像这样

 Exception in thread "main" java.lang.RuntimeException
at Main$ContractLineItem.getDataTablesRow(Main.java:77)
at Main$$Lambda$1.apply(Unknown Source)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:87)
at java.util.Arrays$ArraySpliterator.forEach(Arrays.java:4551)
at java.util.stream.AbstractPipeline$PipelineHelperImpl.into(AbstractPipeline.java:197)
at java.util.stream.op.ForEachOp.evaluateSequential(ForEachOp.java:86)
at java.util.stream.op.ForEachOp.evaluateSequential(ForEachOp.java:37)
at java.util.stream.AbstractPipeline.pipeline(AbstractPipeline.java:336)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:142)
at java.util.Collection.addAll(Collection.java:505)
at java.util.ArrayList.addAll(ArrayList.java)
at java.util.stream.ReferencePipeline.into(ReferencePipeline.java:189)
at Main.map(Main.java:34)
at Main.extractDataTableRows(Main.java:39)
at Main.main(Main.java:29)

虽然@Edwin 的回答是最短的,但它是最难调试和维护的,因为有很多“魔法”来实现它。这对于单元测试很好,但恕我直言,您不希望在生产代码中使用它。

 Exception in thread "main" ch.lambdaj.function.argument.InvocationException: Failed invocation of public java.lang.String[] Main$ContractLineItem.getDataTablesRow() on object Main$ContractLineItem@1d724f31 caused by: null
at ch.lambdaj.function.argument.Invocation.invokeOn(Invocation.java:70)
at ch.lambdaj.function.argument.InvocationSequence.invokeOn(InvocationSequence.java:91)
at ch.lambdaj.function.argument.InvocationSequence.invokeOn(InvocationSequence.java:85)
at ch.lambdaj.function.argument.Argument.evaluate(Argument.java:35)
at ch.lambdaj.function.convert.ArgumentConverter.convert(ArgumentConverter.java:36)
at ch.lambdaj.function.convert.ConverterIterator.next(ConverterIterator.java:37)
at ch.lambdaj.Lambda.convert(Lambda.java:986)
at ch.lambdaj.Lambda.extract(Lambda.java:1035)
at Main.extractDataTableRows(Main.java:49)
at Main.main(Main.java:27)
 Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:474)
at ch.lambdaj.function.argument.Invocation.invokeOn(Invocation.java:68)
... 14 more
 Caused by: java.lang.RuntimeException
at Main$ContractLineItem.getDataTablesRow(Main.java:85)
... 19 more

这种语法需要一些时间才能使用,但我想随着他们收紧语法并删除一些样板代码,它可能更具可读性。

这是一个很好的比较Java 8 Lambda vs LambdaJ vs Guava vs Iterative approach 原文为俄语,请原谅谷歌翻译;)

【讨论】:

  • 我更喜欢更长的更具描述性的变量名称,而不是简短而神秘的变量名称,我认为共识是应该避免使用像“ret”这样的 sohrt 名称。我不关心方法的字符数或大小,我更关心通过避免 for 循环(或更精确地探索是否可以使用 lambda)来提高此(和类似)方法的可读性
  • @AshkanAryan:“我认为共识是应该避免使用像“ret”这样的 sohrt 名称。 ret 很简短,直截了当:这是您的返回值。我实际上不喜欢 longAsADayWithoutWine 变量名 ;)
  • @AshkanAryan 一致认为,在 Java 5.0 到 7 中使用 Lambda 与可读性无关,因为它与使用函数式设计有关。 Guava 自己的文档也说明了这一点。见上文。
  • 可惜我不能+2,我没想到你会花时间写一个番石榴示例:p
  • @fge 好久没用了,不试就敲不下来。如果我先阅读了文档,我可能只是引用了它们。 ;)
【解决方案2】:

怎么样

List<String[]> items = extract(contractLineItems, on(ContractLineItem.class).getDataTablesRow());

LambaJ 带有一个名为 extract 的方法,它是一个映射器。您还可以阅读有关converting objects with lambdaj 的参考资料。

【讨论】:

  • 我猜你的意思是getDataTablesRow
猜你喜欢
  • 1970-01-01
  • 2013-01-10
  • 1970-01-01
  • 1970-01-01
  • 2015-03-26
  • 2019-10-01
  • 1970-01-01
  • 2011-10-28
  • 2013-11-07
相关资源
最近更新 更多