我觉得这里有几个问题,可能会让人们对为什么需要做什么产生一些困惑。
“我以为数组的asList()方法会将数组的值克隆到列表中,但我不明白为什么在代码结尾的toArray()方法中要再次这样做。”
这可能只是打字时的问题,但应该明确指出,您并没有克隆数组中的对象,而只是创建了一个新的列表,并将其中的对象引用指向数组中的对象。这些对象本身将是数组和列表中相同的。我认为这可能是您的意思,但术语在这里可能会比较棘手。
“我以为数组的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的一部分,其中泛型通过类型擦除工作,不会在运行时保留,并且数组的特定工作方式都会使开发人员感到惊讶。
有点抽象正在泄漏。
但可能还有第二个原因。如果你去查看
Java API中的toArray(T[] a)
方法,你会注意到这部分内容:
如果列表适合指定的数组,则返回该列表。否则,将分配一个新数组,其运行时类型为指定数组的类型,并具有此列表的大小。
假设另一个开发人员编写的代码使用您的stableSort方法,如下所示:
T[] items;
// items is created and filled...
T[] sortedItems = stableSort(items);
如果你没有进行克隆,你的代码将会是这样的:
List<T> list = new ArrayList<T>(Arrays.asList(items));
T[] result = list.toArray(items);
现在,您的代码的调用者会得到
sortedItems
,但该数组与他传入的数组相同,即
items
。您看,可变参数仅仅是带有数组参数的方法的语法糖,并且是以这种方式实现的。也许调用者没有预料到他传入的数组会被改变,可能仍然需要具有原始顺序的数组。首先进行克隆将避免这种情况并使方法的效果更少令人惊讶。在这种情况下,良好的方法文档非常重要。
测试您的任务实现代码的可能需要不同的数组作为返回值,这是您的方法必须遵守的实际需求。
编辑:
实际上,您的代码可以更简单。您可以通过以下方式实现相同的效果:
T[] copy = items.clone();
Arrays.sort(copy);
return copy;
但是你的任务可能是要自己实现一个排序算法,所以这一点可能无关紧要。
asList
返回List
,而toArray
返回数组。 - Andremoniy