我可以在 java 中将算法作为参数传递吗?
Can I pass an algorithm as a parameter in java?
我正在对排序算法进行一些测量测试。
我创建了这个方法来计算选择排序需要排序数组的时间
public static double timeToSelectionSort(Double[] arrayOfNumbers) {
double timeToSelectionSort =0;
Stopwatch stopwatch = new Stopwatch();
Selection.sort(arrayOfNumbers);
timeToSelectionSort = stopwatch.elapsedTime();
return timeToSelectionSort;
}
问题是我需要为我的所有排序算法(插入、选择、快速排序、合并排序...)创建此方法
有没有办法将这些算法作为此方法的参数传递?
是的,绝对是。这就是所谓的 Strategy Pattern。基本上,创建一个接口,让每个算法都是实现接口的 class,并且参数是参数的类型(我在这里使用 C# 约定)
public interface SortingAlgo {
void sort(...);
}
public class QuickSort implements SortingAlgo {
public void sort(...) {
...
}
}
public void methodYouWantToAcceptAlgo(SortingAlgo algo) {
...
}
等等
如果您的排序算法都是在不同的 类 中实现的,请使用要调用的方法创建一个接口,并让所有 类 实现该接口。
例如。这就是 List
是一个具有多个具有不同性能特征的实现的接口。
public interface Sort {
void sort(Double[]);
}
public class Selection implements Sort {
// code here
}
public static double timeToSort(Double[] input, Sort sort) {
Stopwatch stopwatch = new Stopwatch();
sort.sort(input);
return stopwatch.elapsedTime();
}
public static void test(Double[] input) {
System.out.println(timeToSort(input, new Selection()));
}
如果您的排序算法都是静态方法,请使用 Consumer<Double[]>
接口,并使用 Java 8 个方法引用提供实现。
public class Sorts {
public static void selection(Double[] input) {
// code here
}
}
public static double timeToSort(Double[] input, Consumer<Double[]> sort) {
Stopwatch stopwatch = new Stopwatch();
sort.accept(input);
return stopwatch.elapsedTime();
}
public static void test(Double[] input) {
System.out.println(timeToSort(input, Sorts::selection));
}
如果您的排序方法并非都共享相同的签名,请使用 lambda 表达式。这是#2 的变体。
public static void test(Double[] input) {
System.out.println(timeToSort(input, (a) -> Sorts.selection(a)));
}
策略模式在这种情况下会非常有用。
定义一族算法,封装每一个,并使它们
可互换。策略让算法独立于
使用它的客户
这些算法可以互换使用以改变应用程序
不改变其架构的行为。
通过单独封装算法,新的算法符合
具有相同的界面,可以很容易地引入。
其他简单的方法是使用Lambda Function
你问的是 strategy pattern,正如 EJoshuaS 指出的那样。策略模式的目的是准确解决您所描述的问题 - 您想要应用 some 操作,但您不一定知道 which 在编译时操作,因此您定义了一个 "strategy" 接口,允许您将 调用 策略的代码与 的代码分开]定义策略。
在您的情况下,您想要对数组进行排序,但希望将其与实际执行排序操作的算法分离。所以,首先你定义一个接口来描述排序:
/** Interface responsible for sorting an array of doubles. */
@FunctionalInterface // we'll come back to this
public interface Sorter {
/** Sorts the input array in ascending order. */
void sort(double[] arr);
}
现在我们有了一个接口,我们可以编写使用该接口的代码,尽管还不知道实现(例如冒泡排序、快速排序等)会是什么:
/** Given an array and a sorting strategy to apply, prints the sorted array. */
public static void printSorted(double[] arr, Sorter sorter) {
sorter.sort(arr);
System.out.println(Arrays.toString(arr));
}
太好了,我们完成了!大多数情况下...现在我们需要创建 Sorter
的一些实现以便调用 printSorted()
。虽然我会将实现本身留给您,但样板文件看起来像:
/** Sorter strategy applying the bubble-sort algorithm. */
public class BubbleSorter implements Sorter {
@Override
public void sort(double[] arr) {
// TODO
}
}
一旦您拥有一个或多个 Sorter
实现,您就可以调用 printSorted()
:
printSorted(new double[] {5.0, 3.0, 10.0}, new BubbleSorter());
任务(打印排序数组)现在与要排序的数组和要使用的排序策略分离,您可以轻松替换不同的策略。
等等,Java 8 呢?
Lambda 和一般的函数式编程本质上是应用策略模式的一种很好的语法(请原谅过于简单化),事实证明它们非常适合这类任务。
我将 Sorter
注释为 @FunctionalInterface
above, which notes that the interface is intended to be used to construct lambda expressions. Any operation that takes a double[]
and doesn't return anything can be passed as a lambda into our printSorted()
function. For example, we can use Arrays.sort()
作为我们的策略:
printSorted(..., d -> Arrays.sort(d));
更好的方法是在此处使用 method reference (Arrays::sort
),但为了清楚起见,我使用了 lambda。
您可能会注意到 Sorter
看起来很像 Consumer<double[]>
。在实践中,它 是 一个 Consumer
,因此我们可以删除该接口并让 printSorted()
采用 Consumer<double[]>
。您应该更喜欢哪个(重用 Consumer
或定义您自己的界面)在很大程度上取决于偏好,但在 different contexts both are reasonable choices.
中
我正在对排序算法进行一些测量测试。
我创建了这个方法来计算选择排序需要排序数组的时间
public static double timeToSelectionSort(Double[] arrayOfNumbers) {
double timeToSelectionSort =0;
Stopwatch stopwatch = new Stopwatch();
Selection.sort(arrayOfNumbers);
timeToSelectionSort = stopwatch.elapsedTime();
return timeToSelectionSort;
}
问题是我需要为我的所有排序算法(插入、选择、快速排序、合并排序...)创建此方法
有没有办法将这些算法作为此方法的参数传递?
是的,绝对是。这就是所谓的 Strategy Pattern。基本上,创建一个接口,让每个算法都是实现接口的 class,并且参数是参数的类型(我在这里使用 C# 约定)
public interface SortingAlgo {
void sort(...);
}
public class QuickSort implements SortingAlgo {
public void sort(...) {
...
}
}
public void methodYouWantToAcceptAlgo(SortingAlgo algo) {
...
}
等等
如果您的排序算法都是在不同的 类 中实现的,请使用要调用的方法创建一个接口,并让所有 类 实现该接口。
例如。这就是List
是一个具有多个具有不同性能特征的实现的接口。public interface Sort { void sort(Double[]); } public class Selection implements Sort { // code here } public static double timeToSort(Double[] input, Sort sort) { Stopwatch stopwatch = new Stopwatch(); sort.sort(input); return stopwatch.elapsedTime(); } public static void test(Double[] input) { System.out.println(timeToSort(input, new Selection())); }
如果您的排序算法都是静态方法,请使用
Consumer<Double[]>
接口,并使用 Java 8 个方法引用提供实现。public class Sorts { public static void selection(Double[] input) { // code here } } public static double timeToSort(Double[] input, Consumer<Double[]> sort) { Stopwatch stopwatch = new Stopwatch(); sort.accept(input); return stopwatch.elapsedTime(); } public static void test(Double[] input) { System.out.println(timeToSort(input, Sorts::selection)); }
如果您的排序方法并非都共享相同的签名,请使用 lambda 表达式。这是#2 的变体。
public static void test(Double[] input) { System.out.println(timeToSort(input, (a) -> Sorts.selection(a))); }
策略模式在这种情况下会非常有用。
定义一族算法,封装每一个,并使它们 可互换。策略让算法独立于 使用它的客户
这些算法可以互换使用以改变应用程序
不改变其架构的行为。通过单独封装算法,新的算法符合
具有相同的界面,可以很容易地引入。
其他简单的方法是使用Lambda Function
你问的是 strategy pattern,正如 EJoshuaS 指出的那样。策略模式的目的是准确解决您所描述的问题 - 您想要应用 some 操作,但您不一定知道 which 在编译时操作,因此您定义了一个 "strategy" 接口,允许您将 调用 策略的代码与 的代码分开]定义策略。
在您的情况下,您想要对数组进行排序,但希望将其与实际执行排序操作的算法分离。所以,首先你定义一个接口来描述排序:
/** Interface responsible for sorting an array of doubles. */
@FunctionalInterface // we'll come back to this
public interface Sorter {
/** Sorts the input array in ascending order. */
void sort(double[] arr);
}
现在我们有了一个接口,我们可以编写使用该接口的代码,尽管还不知道实现(例如冒泡排序、快速排序等)会是什么:
/** Given an array and a sorting strategy to apply, prints the sorted array. */
public static void printSorted(double[] arr, Sorter sorter) {
sorter.sort(arr);
System.out.println(Arrays.toString(arr));
}
太好了,我们完成了!大多数情况下...现在我们需要创建 Sorter
的一些实现以便调用 printSorted()
。虽然我会将实现本身留给您,但样板文件看起来像:
/** Sorter strategy applying the bubble-sort algorithm. */
public class BubbleSorter implements Sorter {
@Override
public void sort(double[] arr) {
// TODO
}
}
一旦您拥有一个或多个 Sorter
实现,您就可以调用 printSorted()
:
printSorted(new double[] {5.0, 3.0, 10.0}, new BubbleSorter());
任务(打印排序数组)现在与要排序的数组和要使用的排序策略分离,您可以轻松替换不同的策略。
等等,Java 8 呢?
Lambda 和一般的函数式编程本质上是应用策略模式的一种很好的语法(请原谅过于简单化),事实证明它们非常适合这类任务。
我将 Sorter
注释为 @FunctionalInterface
above, which notes that the interface is intended to be used to construct lambda expressions. Any operation that takes a double[]
and doesn't return anything can be passed as a lambda into our printSorted()
function. For example, we can use Arrays.sort()
作为我们的策略:
printSorted(..., d -> Arrays.sort(d));
更好的方法是在此处使用 method reference (Arrays::sort
),但为了清楚起见,我使用了 lambda。
您可能会注意到 Sorter
看起来很像 Consumer<double[]>
。在实践中,它 是 一个 Consumer
,因此我们可以删除该接口并让 printSorted()
采用 Consumer<double[]>
。您应该更喜欢哪个(重用 Consumer
或定义您自己的界面)在很大程度上取决于偏好,但在 different contexts both are reasonable choices.