无法理解lambda和长流方法

时间:2019-05-26 10:42:05

标签: java

我有这样的代码示例。

import java.util.LinkedList;
import java.util.List;
import java.util.stream.LongStream;

public class RemovedNumbers {

    public static List<long[]> removNb(long n) {
        System.out.println(n);
        long sum = ((n + 1) * n / 2);
        long minMultiplication = sum - 2 * n + 1;
        long minCandidate = (long) Math.sqrt(minMultiplication);

        LinkedList<long[]> list = new LinkedList<>();

        LongStream.rangeClosed(minCandidate, n)
                  .mapToObj(a -> new long[]{a, calculateB(a, sum)})
                  .filter(longs -> longs[0] > longs[1])
                  .filter(longs -> longs[1] <= n)
                  .filter(longs -> longs[0] * longs[1] == sum - longs[0] - longs[1])
                  .forEach(longs -> addArrays(list, longs));

        return list;
    }

    private static long calculateB(long a, long sum) {
        return (sum - a) / (a + 1);
    }

    private static void addArrays(final LinkedList<long[]> list, final long[] longs) {
        list.addFirst(new long[]{longs[1], longs[0]});
        list.add(longs);
    }
}

在LongStream部分,这段代码对我来说很复杂。 我没有得到什么要点,所以我需要帮助:

  1. 我检查LongStream类。
  2. 此类使用四种方法:rangeClosed,mapToObj,filter,forEach(我在Java文档中找到了它们的描述)。不幸的是,现在我开始研究Java 1.8版本,因此我不明白它的工作原理和发生的情况。
  3. mapToObj中"a"出现在哪里?它是什么?我在代码的上半部分没有看到var "a"声明。
  4. 当我得到lambda时,是通过以下方案制作的:(arguments) -> (body)。因此"a"是一个参数,"new long[]..."-是一个主体。这部分对我来说没有任何问题。但是下一个参数whereis "longs"-体“ longs [0]> longs [1]”(正文)引起了一些问题。什么是var "longs"?过去没有声明!它怎么出现的?怎么运行的?
  5. 我可以将LongStream类写在一行中吗?像:LongStream.rangeClosed().filter().filter().filter().forEach(); ?
    1. 我正确地说所有方法都可以执行吗?彼此之间?第一个范围已关闭,然后是mapToObj,然后是过滤器...还是还有其他顺序?

非常感谢!

4 个答案:

答案 0 :(得分:2)

您的第三点回答您的第二点-a是传递给mapToObj的lambda表达式的参数。

如果您能理解,那么您的第四点也应该很容易理解。 longs是传递给filter的lambda表达式的参数。请记住,您可以随意命名参数名称。我想代码的作者将参数重命名为longs的原因是因为在上一行中,流中的每个long都映射到long[]中,所以现在它是一个流长数组。

  

我可以将LongStream类写在一行中吗?

是的,但是您最终会得到超长的代码行,因此我们几乎从不这样做。

  

我正确地说所有方法都可以执行吗?彼此之间?第一个范围已关闭,然后是mapToObj,然后是过滤器...还是还有其他顺序?

该方法被调用,但是它们执行的操作不会立即运行。这是流的很酷的部分。仅当您执行mapToObj(一种终端操作)时,才对filterforEach进行加长。换句话说,mapToObjfilter有点像在说“这是此流应该做的...”,而当您执行forEach时,您在说“现在就做! “

如果您仍然不知道流在做什么,请尝试将其视为工厂中的生产线。开始时,longs位于传送带上。然后它们经过一台机器,将它们每个都转换为long[]。之后,它们会通过三个过滤器。除非长阵列满足某些条件,否则这些过滤器会将它们推离传送带。

编辑:

如果您想编写不带lambda的代码,则可以使用匿名类来编写它:

LongStream.rangeClosed(minCandidate, n)
        .mapToObj(new LongFunction<long[]>() {
            @Override
            public long[] apply(long a) {
                return new long[]{a, calculateB(a, sum)};
            }
        })
        .filter(new Predicate<long[]>() {
            @Override
            public boolean test(long[] longs) {
                return longs[0] > longs[1] && 
                        longs[1] <= n && 
                        longs[0] * longs[1] == sum - longs[0] - longs[1];
            }
        })
        .forEach(new Consumer<long[]>() {
            @Override
            public void accept(long[] longs) {
                addArrays(list, longs);
            }
        });

答案 1 :(得分:0)

每个lambda表达式都实现一个功能接口,或者更具体地说,它实现该功能接口的单个​​抽象方法。

因此,在a -> new long[]{a, calculateB(a, sum)}中,a是功能接口实现的方法的参数。由于mapToObj接受类型为LongFunction的参数,因此该lambda表达式实现了该接口的R apply(long value)方法,这意味着lambda表达式也可以写为(long a) -> new long[]{a, calculateB(a, sum)}

mapToObj调用将LongStream转换为Stream<long[]>,因此随后的filter调用-longs -> longs[0] > longs[1]的lambda表达式也可以写成{ {1}}-它实现了功能接口(long[] longs) -> longs[0] > longs[1],这意味着它实现了Predicate<long[]>

是的,您可以在一行中编写整个流管道,但是将其分成多行更容易阅读。

  

我正确地说所有方法都可以执行吗?彼此之间?第一个范围已关闭,然后是mapToObj,然后是过滤器...或者还有其他顺序

不完全是。尽管每个中间方法都会产生一个输出,用作下一个方法的输入,但是这些方法的评估仅在执行终端操作(在这种情况下为boolean test(long[] t))之后才开始。并且这些操作不一定处理forEach的所有元素。例如,如果终端操作将是Stream而不是firstFirst(),则管道将仅处理足够的元素,直到找到通过所有过滤器的第一个元素。

答案 2 :(得分:0)

  

我可以将LongStream类写在一行中吗?

您在这里看到的是method chaining。这是方法之间可以彼此链接的地方。几乎所有的类都可以做到这一点。

Sweeper几乎回答了其他所有问题。

答案 3 :(得分:0)

3和4:

您正在尝试了解lambda的工作原理,因此我将为您分解代码:

// this return a LongStream obj 
LongStream.rangeClosed(minCandidate, n)
// so with point notation you can access to one of the method in LongStream
// matToObj in this case.
.mapToObj(a -> new long[]{a, calculateB(a, sum)}) 

什么是?什么->?还有什么其他东西?

MapToObj使用一个IntFunction映射器参数,而 a 是即时声明该类型的,这就是为什么您之前在代码中没有看到它的原因。 箭头指示正确的站点是lamba表达式,如果您有内联操作,则可以省略return语句,并且不能包含{}括号,因此可以将该语句想像为return语句。 使用lamba函数,您可以轻松创建操作链,这就是为什么您拥有许多个又一个函数的原因。您必须记住,下一个函数将与上一个函数的返回类型相同的对象类型作为参数。