我正在对排序算法进行一些测量测试。
我创建了这个方法来计算选择排序需要订购数组的时间
public static double timeToSelectionSort(Double[] arrayOfNumbers) {
double timeToSelectionSort =0;
Stopwatch stopwatch = new Stopwatch();
Selection.sort(arrayOfNumbers);
timeToSelectionSort = stopwatch.elapsedTime();
return timeToSelectionSort;
}
问题是我需要为我的所有排序算法(插入,选择,快速排序,合并排序......)创建此方法。
有没有办法将这些算法作为此方法的参数传递?
答案 0 :(得分:6)
是的,绝对的。这就是所谓的Strategy Pattern。基本上,创建一个接口,让每个算法都是一个实现接口的类,并使参数类型为参数(我在这里使用C#约定)
public interface SortingAlgo {
void sort(...);
}
public class QuickSort implements SortingAlgo {
public void sort(...) {
...
}
}
public void methodYouWantToAcceptAlgo(SortingAlgo algo) {
...
}
等
答案 1 :(得分:1)
如果您的排序算法全部在不同的类中实现,请使用要调用的方法创建一个接口,并让所有类实现该接口。
例如。这就是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)));
}
答案 2 :(得分:0)
战略模式在这种情况下非常有用。
定义一系列算法,封装每个算法并制作它们 互换。策略让算法独立于 使用它的客户
算法可以互换使用以改变应用程序 不改变其架构的行为。
通过单独封装算法,新算法符合要求 可以很容易地引入相同的界面。
其他简单方法是使用Lambda Function
答案 3 :(得分:0)
正如EJoshuaS所说,你正在询问strategy pattern。策略模式的目的是准确解决您所描述的问题 - 您希望应用某些操作,但您不一定知道 在编译时操作,因此您定义了一个&#34;策略&#34;界面,允许您将调用策略的代码与定义策略的代码分开。
在您的情况下,您希望对数组进行排序,但希望将其与哪个算法实际执行排序操作分离。因此,首先定义一个接口来描述排序:
/** 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
,注意到该接口旨在用于构造lambda表达式。任何需要double[]
并且不返回任何内容的操作都可以作为lambda传递到我们的printSorted()
函数中。例如,我们可以使用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中。