直接操纵数据是不好的做法,如:
Sorter.mergeSort(testData); //(testData is now sorted)
或者我应该创建数据的副本,然后操作并返回如下:
sortedData = Sorter.mergeSort(testData); // (sortedData is now sorted and testData remains unsorted)?
我有几种排序方法,我希望它们在操作数据方面保持一致。使用我的insertSort方法,我可以直接处理未排序的数据。但是,如果我想保持未分类的数据不变,那么我将不得不在insertionSort方法中创建未排序数据的副本并操纵并返回它(这似乎是不必要的)。另一方面,在我的mergeSort方法中,我需要以这样或那样的方式创建未排序数据的副本,所以我最终做了一些似乎没有必要作为返回新的sortedList的工作:
List <Comparable> sorted = mergeSortHelper(target);
target.clear();
target.addAll(sorted);`
请让我知道哪种方法更好,谢谢!
答案 0 :(得分:5)
这取决于您是在优化性能还是功能纯度。通常在Java中不强调功能纯度,例如Collections.Sort对您给出的列表进行排序(即使它是通过首先进行数组复制来实现的)。
我会优化性能,因为这看起来更像典型的Java,任何想要的人都可以随时复制该集合,例如Sorter.mergeSort(new ArrayList(testData));
答案 1 :(得分:2)
最佳做法是保持一致。
我个人更喜欢我的方法不修改输入参数,因为它可能并不适用于所有情况(如果他们需要保留原始顺序,你可以将责任推到最终用户上进行复制)。
话虽如此,修改输入有明显的性能优势(特别是对于大型列表)。所以这可能适合您的应用程序。
只要最终用户能够清楚地了解您的功能,您就可以获得任何一种方式!
答案 2 :(得分:2)
在Java中,我通常提供两种选择(无论如何,在编写可重用的实用程序方法时):
/** Return a sorted copy of the data from col. */
public List<T> mergeSort(Collection<T extends Comparable<T>> col);
/** Sort the data in col in place. */
public void mergeSortIn(List<T extends Comparable<T>> col);
我在这里做了一些关于签名和类型的假设。也就是说,Java规范通常是 - 或者至少是* - 通常会使状态发生变异。这通常是危险的,尤其是跨越API边界 - 例如通过“客户端”代码更改传递到库的集合。特别是最小化整体状态空间和可变状态通常是设计良好的应用程序/库的标志。
听起来您想要重复使用相同的测试数据。为此,我将编写一个构建测试数据并返回它的方法。这样,如果我在不同的测试中再次需要相同的测试数据(即在同一数据上测试mergeSort()/ insertionSort()实现),您只需构建并再次返回它。我通常在编写单元测试时这样做(例如在JUnit中)。
无论哪种方式,如果您的代码是其他人使用的库类/方法,您应该清楚地记录其行为。
除此之外:在“真实”代码中,实际上没有任何理由指定合并排序是使用的实现。调用者应该关心它做什么,而不是 它做什么 - 所以名称通常不是mergeSort(),insertionSort()等。
(*)在一些较新的JVM语言中,有意识地从可变数据移开。 Clojure完全没有可变状态,因为它是一种纯函数式编程语言(至少在正常的单线程应用程序开发中)。 Scala提供了一组并行的集合库,它们不会改变集合的状态。这在多线程,多处理器应用程序中具有主要优势。由于集合使用了聪明的算法,这并不像天真预期的那样耗费时间/空间。
答案 3 :(得分:0)
在您的特定情况下,修改“实际”数据会更有效。您正在对数据进行排序,观察到它对排序数据而不是未排序数据的处理效率更高。所以,我不明白你为什么要保留未分类的数据。看看Why is it faster to process a sorted array than an unsorted array?
答案 4 :(得分:0)
应该在函数中操纵可变对象。与Arrays#sort
但是不可变对象(如String)只能返回“新”对象。与String#replace