不同类的对象使用相同的排序算法(Java)



现在我有一个排序算法,它以随机顺序获取一个对象数组,根据值属性将它们排序为一个新数组,并返回排序后的数组(特别是ArrayList,尽管我认为它与我的问题无关)。在我的任务中,我现在被指示根据这些不同对象自身的值属性,从高到低对另一个不同对象数组进行排序。我不想复制粘贴我已经实现的算法,也不想通过在几个地方更改对象和属性名称,或者直接按O(n^2)顺序对其他对象进行排序,而是想以更通用的方式重用我当前的算法。更清楚的是:

new Array<ClassA> a;
new Array<ClassB> b;
public Array<ClassB> sort(Array<ClassB> input){
//sort based on certain attribute in ClassB;
return sortedArray;
}

我想变成:

new Array<ClassA> a;
new Array<ClassB> b;
public Array<ClassB> sort(Array<Object> input, Attribute x){
//sort based on attribute of Object where which attribute is given as a parameter x;
return sortedArray;
}

我知道这可能比我的书面解释更令人困惑,所以为了更清楚(我希望),我希望将我的排序算法变成类似于python的内置函数:排序(可迭代,key=None,reverse=False)

我实现的算法是任务的一部分,所以我不能用库中的东西来代替它。如果你认为我的整个问题都很愚蠢,那么请考虑一下英语不是我的母语,不——我对编程术语一点也不熟悉,是的——我是一个非常糟糕的程序员。那就继续你的一天吧!如果你认为你能给我一个合理的答案,我将不胜感激:D

public Array<ClassB> sort(Array<Object> input, Attribute x){

这将任意对象的数组转换为包含ClassB实例的数组,这显然不影响它的工作方式。

Array<Object> input

这并不意味着你认为它意味着什么。Array<ClassB>NOTArray<Object>的有效值。毕竟,我可以向Array<Object>添加String(String是一个对象!),但不能向Array<ClassB>添加。在计算机技术术语中,java中的基本类型(如NumberInteger)是协变-任何作为X的子类型的实例也是X:整数也是数字,也是对象。但是类型args是不变量。任何属于X子类型的实例都与X不兼容:List<Integer>不是List<Number>。这是因为它就是这样工作的,正如我上面所展示的:您可以向List<Number>添加doubles。

您可以选择不同的方差:List<? extends Number>List<? super Number>这样做。不过,你不需要它来解决这个问题。

泛型将事物联系起来。这就是你想要的:接受一些。。thing,并返回相同事物的数组:

public <T> Array<T> sort(Array<T> input, Attribute x) {}

上面写着:有一些类型的T。不知道它是什么。这个T用来连接事物:输入数组的类型是什么?输出数组具有相同的类型。

这就是你想要的。

Attribute x

不可能以编程方式引用属性。幸运的是,您实际上并不需要属性。您需要一个函数:给定一些T,提供一个值;一个你现在如何内在地排序的。这个功能是如何工作的?你不在乎。只要它是一致的(对于任何给定的实例返回相同的值),就可以根据映射的值对其进行排序。所以,你不想要Attribute,你想要一个函数,把t变成你可以排序的东西

尽情狂欢吧,列出你拥有的每一个可分类的概念

public <T> Array<T> sortByInt(Array<T> input, ToIntFunction<T> f) {}
public <T> Array<T> sortByString(Array<T> input, Function<T, String> f) {}
public <T> Array<T> sortByDouble(Array<T> input, ToDoubleFunction<T, String> f) {}

等等。这些方法确实需要不同的名称,您不希望在这里重载。

映射到对象并祈祷

你不知道如何对任何给定的对象进行排序。您大概只知道如何对integer、double和string进行排序。你可以写:

public <T> Array<T> sortByDouble(Array<T> input, Function<T, ?> f) {}

然后在运行时检查函数是否映射到您想要的。如果函数映射到InputStream,这显然不是一个可以排序的东西,那么您必须检查它,并抛出异常。如果一个对象被映射到int,另一个对象映射到字符串,也会让人很困惑。这听起来很简单,但事实并非如此。不要这样做。

使用java内置的自定义内容概念

Java对此有一个类型:Comparable。这变得有点复杂;在这里,你希望对象可以将自己与自己进行比较,所以:

public <T, V extends Comparable<V>> Array<T> sort(Array<T> input, Function<T, V> f) {}

这有点复杂,但这是最正确的版本。让我们来看看:

  • <T, V... >部分:这说明这个方法有2个类型变量。它们就像普通变量,只是它们包含类型
  • T:它没有任何界限。因此,它可以是任何东西。任何调用此方法的代码都会显式地选择某个内容,或者编译器会找出它
  • V:这是有界的。V不可能只是任何东西。V必须是某种类型,它具有实现Comparable<V>的属性——换句话说,它可以将自己与自己类型的另一个值进行比较。StringInteger和许多其他内置到java中的东西都是这样的。CCD_ 27是用于填写CCD_ 28的有效类型
  • Array<T>-方法返回的内容
  • sort-方法名称
  • Array<T> input—输入。请注意,此链接到返回类型
  • Function<T, V>-一个函数。这个函数将接收一个T类型的对象,并返回一个V类型的对象。记住,V不能只是任何东西——它需要是某种特定的类型,可以对自己与其他相同类型的对象进行排序,例如StringInteger

有了这些,您可以编写一些代码。例如:

T first = input.get(0); // still no idea what T is. But `input.get(0)` returns it.
T second = input.get(1);
V firstAttribute = function.apply(first); // extracts the sortable attribute
V secondAttribute = function.apply(second);
int ordering = firstAttribute.compareTo(secondAttribute); // magic!

最后一行是一个奇迹。它编译并保证工作:firstAttribute是V,虽然我们不知道V是什么(可以是String。也可以是Integer),但我们知道V有int compareTo(V other)方法。因此,我们可以援引这一点。负数表示firstsecond之前,正数表示first在第二之后,而0响应表示firstsecond相等,或者就比较而言,是兄弟姐妹——两者都不"在"另一个之前。

这个函数就是编写排序算法所需要的全部功能。

注意,假设firstT,您可以写:

Array<T> output = new Array<T>();
output.add(first);

编译器不知道T是什么,但它知道first是T,所以这个调用是允许的。

最新更新