Java中两个ArrayList之间的减法



有什么快速函数可以执行以下代码吗?

ArrayList<Double> A = new ArrayList<>();
ArrayList<Double> B = new ArrayList<>();
//ignore adding steps
// A = [1,2,3,4,5]
// B = [0,1,2,3,4]
C = A - B
// get C = [1,1,1,1,1]

TL;DR:请参阅下面两个完整的代码示例,以及它们章节的标题以了解上下文。

方法

强制性方法

命令式编程通常被描述为";指示各个步骤以实现某一目标";。

我们可以使用for循环(一种与命令式编程相关的语言特性)来遍历列表AB,并收集列表C:中它们元素的差异

import java.util.ArrayList;
import java.util.Arrays;
public class ImperativeApproach {
public static void main(String[] args) {
ArrayList<Double> A = new ArrayList<>(Arrays.asList(new Double[] {1d,2d,3d,4d,5d}));
ArrayList<Double> B = new ArrayList<>(Arrays.asList(new Double[] {0d,1d,2d,3d,4d}));

ArrayList<Double> C = new ArrayList<>();

assert A.size() == B.size();
int size = A.size();
for (int i = 0; i < size; ++i) {
C.add(A.get(i) - B.get(i));
}

System.out.println(C); // [1.0, 1.0, 1.0, 1.0, 1.0]
}
}

功能性方法

函数式编程比命令式编程更具声明性:它可以被描述为";在以下特定步骤下请求特定结果";。

函数式编程的特点通常包括高阶函数和作为一级公民的函数、函数组合、lambda等等。

旁白:注意命令式函数式编程都只是范例;通过某些"分类"来对代码或语言进行分类的方法;特征";。示例:随着Streams(见下文)添加到Java中,用Java编写函数代码变得更加容易。

如前所述,我们可以使用Java示例:

  1. 创建要访问A/B的索引流;即0到size of A or B
  2. 将索引映射到AB的元素的差异
  3. 将(流式传输的)结果收集为名为C的列表
import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.IntStream;
public class FunctionalApproach {
public static void main(String[] args) {
ArrayList<Double> A = new ArrayList<>(Arrays.asList(new Double[] {1d,2d,3d,4d,5d}));
ArrayList<Double> B = new ArrayList<>(Arrays.asList(new Double[] {0d,1d,2d,3d,4d}));

assert A.size() == B.size();
int size = A.size();
Double[] arrayC = IntStream.range(0, size)
.mapToDouble((index) -> A.get(index) - B.get(index))
.boxed()
.toArray(Double[]::new);

ArrayList<Double> C = new ArrayList<>(Arrays.asList(arrayC));

System.out.println(C); // [1.0, 1.0, 1.0, 1.0, 1.0]
}
}

(@GrzegorzPiwowarek在一个类似但古老的问题上提供了替代方案,可以说是更好的实现。)

附录

上面的例子中可能有一些不熟悉的表达方式,我将在本节中对此进行解释。

断言

Java中的断言是一种开发工具,可以确保代码的正确功能。如果断言失败,它们将抛出异常。只有在正确编译并启用它们的情况下,它们才会运行。

请注意,断言不能取代正确的检查Oracle有一个关于何时使用断言的摘要。

在上面的例子中,我使用断言主要在语义上描述;以下代码仅在断言的条件"下工作;。

基本体和装箱值

Java中的(自动)装箱指的是将基元值转换为其相应的包装类。

在不能使用基元的地方,或者在需要Object的地方,这是必需的。例如像List这样的泛型,您可以在Oracle的自动装箱教程中找到简单的解释。

如果在您的代码中,对此类包装类的引用可能为null,请格外小心:Java无法将null取消装箱到基元,相反,它将引发运行时异常在上面的代码中,不可能出现这样的情况。

在这两种方法中

由于数组更具可读性和静态编译性,我选择将列表的初始值作为数组提供。我的实现带有以下要求:

  1. 用初始值构造ArrayList需要Collection[1]
  2. Arrays.asList()返回一个Collection,但需要一个非基元数组(这里:带框值的数组;另外:带框数组)[2]
  3. 初始化盒装数组(Double[])需要元素具有相应的基元类型;我们需要提供类型为CCD_ 17的数字[3]
  4. 将数字文字声明为double需要我们在它们后面加上d(或使用小数点)[4]

这意味着这个无效代码:

ArrayList<double> A = new double[]{1, 2, 3, 4, 5};

需要写入,例如以下内容才能有效:

ArrayList<Double> A = new ArrayList<>(Arrays.asList(new Double[]{1d, 2d, 3d, 4d, 5d}));

在命令式方法中

在命令式方法的示例中不需要进一步的代码更改。将数字添加到列表C会自动装箱数字,因为这里没有使用像new Double[]这样具有特定限制的中间对象。

在功能方法中

Streams示例中,IntStream.mapToDouble()返回一个DoubleStream(作用于基元的流)。它的toArray()函数将返回一个基元数组(double[]),如前所述,我们无法轻松地将其提供给列表C的构造。

因此,我们首先需要将其装箱(请参见DoubleStream.boxed()),它将返回一个Stream<Double>。然后可以将该流的元素收集到一个装箱的数组中(通过提供数组的构造函数Double[]::new;请参阅Oracle的教程"方法引用")。

然后,可以使用这样的数组来构建如前所述的列表。

您必须循环遍历两个数组的每个元素,并从另一个数组中的子数组中减去它。请找到下面的工作代码片段。

import java.util.*;
class HelloWorld {
public static void main(String[] args) {
int[] list1 = {1,2,3,4,5};
int[] list2 = {0,1,2,3,4};
int[] list3 = {0,0,0,0,0};

for(int i =0; i< list1.length; i++)
{
list3[i] = list1[i] - list2[i];
}

for(int i =0; i< list3.length; i++)
{
System.out.print(list3[i]);
}

}
}

这将为您提供以下输出:

[1,1,1,1]

最新更新