我有这样的代码示例。
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部分,这段代码对我来说很复杂。 我没有得到什么要点,所以我需要帮助:
"a"
出现在哪里?它是什么?我在代码的上半部分没有看到var "a"
声明。 (arguments) -> (body)
。因此"a"
是一个参数,"new long[]..."
-是一个主体。这部分对我来说没有任何问题。但是下一个参数whereis "longs"
-体“ longs [0]> longs [1]”(正文)引起了一些问题。什么是var "longs"
?过去没有声明!它怎么出现的?怎么运行的? LongStream.rangeClosed().filter().filter().filter().forEach(); ?
非常感谢!
答案 0 :(得分:2)
您的第三点回答您的第二点-a
是传递给mapToObj
的lambda表达式的参数。
如果您能理解,那么您的第四点也应该很容易理解。 longs
是传递给filter
的lambda表达式的参数。请记住,您可以随意命名参数名称。我想代码的作者将参数重命名为longs
的原因是因为在上一行中,流中的每个long
都映射到long[]
中,所以现在它是一个流长数组。
我可以将LongStream类写在一行中吗?
是的,但是您最终会得到超长的代码行,因此我们几乎从不这样做。
我正确地说所有方法都可以执行吗?彼此之间?第一个范围已关闭,然后是mapToObj,然后是过滤器...还是还有其他顺序?
该方法被调用,但是它们执行的操作不会立即运行。这是流的很酷的部分。仅当您执行mapToObj
(一种终端操作)时,才对filter
和forEach
进行加长。换句话说,mapToObj
和filter
有点像在说“这是此流应该做的...”,而当您执行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)
答案 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函数,您可以轻松创建操作链,这就是为什么您拥有许多个又一个函数的原因。您必须记住,下一个函数将与上一个函数的返回类型相同的对象类型作为参数。