我昨天在写作业时遇到了问题。我完成了作业,但我仍然不明白为什么我的代码有效。我不得不编写一个sort函数,它将任何类似泛型对象的变量作为参数并返回参数。问题是我必须返回一个排序对象数组。所以我必须学习更多关于varargs列表和数组的知识。
该功能定义如下。
public <T extends Comparable<T>> T[] stableSort(T ... items)
并在函数内部创建了一个列表,我将对其进行排序并完成所有工作。
List<T> list = new ArrayList<T>(Arrays.asList(items));
在函数结束时我返回列表toArray,使其与输出类型T []匹配。
list.toArray(items.clone());
我的问题是因为我已经从varargs中创建了列表,为什么我必须在toArray函数中执行items.clone()。这似乎对我做了两件事。我以为arrays.asList()会将数组的值克隆到列表中,我不知道为什么我在toArray()的代码末尾再次这样做。我知道这是写它的正确方法,因为我昨天完成了作业,并从班级论坛中找到了这种方式,但我仍然不明白为什么。
修改
这项任务要求我创建一个包含已排序文件的新数组,然后返回它。由于类型擦除,如果不引用适合泛型的类,则无法实例化泛型类型的数组。但是,varargs数组的类型为T,所以我应该克隆一个符合通用约束的类型数组。我不知道该怎么办。所以我决定使用列表让我的时间更容易,直到截止日期。
答案 0 :(得分:3)
我的问题是因为我已经从varargs列出了这个列表,为什么我要做items.clone()
你是对的。不幸的是,如果您只是使用toArray()
方法,编译器将无法确定数组的类型。您应该收到编译错误,指出无法从Object []转换为T [] 。需要调用item.clone()
来帮助编译器进行类型推断。另一种方法是说return (T[])list.toArray
那就是说,我不推荐任何一种方法。将数组转换为列表并首先将其转换回数组并没有多大意义。我没有看到你从这段代码中理解的任何重大收获。
答案 1 :(得分:2)
在我看来,这里有一些问题,可能会产生一些混淆,为什么需要做什么。
我认为arrays.asList()会将数组的值克隆到列表中,但我不知道为什么我会在toArray()的代码末尾再次执行此操作。
这可能就像它的输入方式一样,但应该清楚的是你没有克隆数组中的对象,而只是创建一个带有对象引用的新List在数组中。对象本身将与List中的数组相同。我相信这可能就是你的意思,但术语在这里可能很棘手。
我认为arrays.asList()会将数组的值克隆到列表...
不是真的。使用Arrays.asList(T[] items)
将在实现java.util.List接口的数组items
上提供视图。这是一个固定大小的列表。你无法添加它。对它的更改(例如替换元素或就地排序)将传递给基础数组。所以,如果你这样做
List<T> l = Arrays.asList(T[] items);
l.set(0, null);
...您只需将实际数组items
的索引0处的元素设置为null。
您执行此操作的代码部分
List<T> list = new ArrayList<T>(Arrays.asList(items));
可写成:
List<T> temp = Arrays.asList(items);
List<T> list = new ArrayList<T>(temp);
第一行是“视图”,第二行将有效地创建一个新的java.util.ArrayList
,并按照迭代器返回的顺序填充视图的值(这只是顺序在数组中)。因此,您现在对list
所做的任何更改都不会更改数组items
,但请记住,它仍然只是一个引用列表。 items
和list
引用了相同的对象,只是按照自己的顺序。
我的问题是因为我已经从varargs中创建了列表,为什么我必须在toArray函数中执行items.clone()。
这里可能有两个原因。第一个是CKing在他/她的回答中说的。由于类型擦除和数组在Java中的实现方式(根据它是基元或引用数组而有单独的数组类型)如果你刚刚调用了toArray()
,JVM就不知道要创建什么类型的数组列表,这就是该方法的返回类型为Object[]
的原因。因此,为了获取特定类型的数组,必须为该方法提供一个数组,该数组可以在运行时用于确定类型。这是Java API的一部分,其中泛型通过类型擦除工作的事实不会在运行时保留,并且数组工作的特定方式会聚集在一起以使开发人员感到惊讶。 A bit of abstraction is leaking there
但可能还有第二个原因。如果你去查看toArray(T[] a)
method in the Java API,你会注意到这一部分:
如果列表适合指定的数组,则返回其中。否则,将使用指定数组的运行时类型和此列表的大小分配新数组。
假设另一个开发人员的一些代码正在使用你的stableSort方法:
T[] items;
// items is created and filled...
T[] sortedItems = stableSort(items);
如果你没有做克隆,你的代码会发生什么:
List<T> list = new ArrayList<T>(Arrays.asList(items));
// List is now a new ArrayList with the same elements as items
// Do some things with list, such as sorting
T[] result = list.toArray(items);
// Seeing how the list would fit in items, since it has the same number of elements,
// result IS in fact items
现在,代码的调用者返回sortedItems
,但该数组与传入的数组相同数组,即items
。你看,varargs只不过是带有数组参数的方法的语法糖,并且是这样实现的。也许调用者不希望他作为参数传入的数组被改变,并且可能仍然需要具有原始顺序的数组。首先执行克隆将避免这种情况,并使该方法的效果不那么令人惊讶。在这种情况下,关于你的方法的良好文档是至关重要的。
测试你的作业实现的代码可能需要一个不同的数组,这是你的方法遵守该合同的实际获取。
编辑:
实际上,您的代码可能更简单。你将实现同样的目标:
T[] copy = items.clone();
Arrays.sort(copy);
return copy;
但是你的任务可能是你自己实际实现了一个排序算法,所以这一点可能没有实际意义。
答案 2 :(得分:0)
你需要使用它:
class myParent
{
public int id = 3;
private string name;
public myParent() : this("Parent class private string")
{
}
protected myParent(string name)
{
this.name = name;
}
public void mymethod()
{
Console.WriteLine("{0} & {1}", name, id);
}
}
class myChild : myParent
{
public myChild() : base("Child class private string")
{
}
}
当您想要进行内联声明时。
例如:
List<T> list = new ArrayList<T>(Arrays.asList(items));
答案 3 :(得分:0)
顺便说一句,你没有必要使用return list.toArray(items.clone());
你可以使用return list.toArray(Arrays.copyOf(items, 0));
,你要传递给list.toArray()
一个包含空数组的空数组没有来自items
的论据。
将参数传递给带有参数的list.toArray()
版本的重点是提供一个数组对象,其实际运行时类是它想要返回的数组对象的实际运行时类。这可以通过items.clone()
或items
本身实现(尽管这会导致list.toArray()
将结果元素写入items
所指向的原始数组中不想发生),或者如上所示,还有一个具有相同运行时类的空数组。
顺便说一下,将参数传递给list.toArray()
的需要根本不是泛型类型的问题。即使你用pre-generics Java编写了这个,你也不得不做同样的事情。这是因为不带参数的List::toArray()
版本总是返回一个实际运行时类为Object[]
的数组对象,因为List
在运行时并不知道它的组件类型是什么是。为了让它返回一个实际运行时类不同的数组对象,你必须给它一个正确的运行时类的示例数组对象。这就是为什么pre-generics Java也有List::toArray()
版本的一个参数;即使在pre-generics中,两个方法都声明返回Object[]
,它们是不同的,因为返回的实际运行时类是不同的。