【问题标题】:Sorting a List takes a lot of time对列表进行排序需要很多时间
【发布时间】:2015-06-01 07:50:30
【问题描述】:

我有以下列表要排序:

  A 0.53
  B 0.56
  C 0.56
  D 0.98
  E 0.33

请注意,我的列表可能包含 1000 条此类记录。我正在对列表进行排序并将排序后的列表放入数组中:

  String str="";
        for(String s: mylist){
            str+=s+",";
        }
        String[] sArr = str.split(",");
        String temp="";
        for(int i=0; i<sArr.length;i++) {
            for(int j= i+1; j<sArr.length;j++){
                if(sArr[i].split("\\s")[1].compareToIgnoreCase(sArr[j].split("\\s")[1])<0){
                    temp= sArr[j];
                    sArr[j]= sArr[i];
                    sArr[i]=temp;
                }
            }
        } 

       //sArr now contains the sorted list

问题是,当我有 1000 条记录时,排序需要很长时间。 我的问题: 有没有其他方法可以在更短的时间内有效地执行相同的任务!还是我的编码方式有问题。有人可以帮帮我吗。

【问题讨论】:

  • 对于初学者,您可以使用预先构建的优化排序方法,例如Arrays.sort...
  • 还使用不同的排序算法,如快速排序、合并排序。也不要在每次比较中打电话给split("\\s")(这是一个非常昂贵的电话)。而是创建单独的类来存储您的值。
  • 您还应该在遍历列表时使用StringBuilder 而不是+=,但是对于 1000 个项目不应该那么明显,但作为最佳实践会更好。字符串操作的数量也会导致排序缓慢,但正如其他人所说,改变你的排序算法,以及
  • 为了进一步说明@sfat 的观点,我会强烈推荐使用 StringBuilder 进行循环使用。循环中的字符串连接(未优化)是二次时间运算,但字符串构建器可以节省工作并且是线性时间运算。如果您的 Java 编译器没有为您进行 StringBuilder 优化,您最终会得到 problem similar to this one in C#

标签: java arrays performance sorting


【解决方案1】:

有很多方法可以对元素列表进行排序。您正在使用插入排序,这是一种慢速排序方法。你可以使用:

Arrays.sort(sArr);

这应该比您的插入排序更快。

如果您想了解更多关于排序算法的信息: wikipedia

【讨论】:

  • 请注意,OP 也需要自定义 Comparator&lt;String&gt;。也许您还应该评估用于比较元素的算法的性能。在这种情况下使用 memoization 甚至可以加快速度。
【解决方案2】:

您的代码依赖于大量字符串操作。字符串在java中是不可变的。例如:如果你有 String a 和 String b 并且你写了 a += b; java不只是将字符串b添加到a。它从 string a + String b 创建一个新的 String 实例,从而产生一个新的 String 对象。这样做一两次没什么大不了的,但您的代码确实严重依赖它。

我的方法是为您的数据创建一个实现 Comarable 接口的新类:

public class DataPoint implements Comparable<DataPoint>{
    public String key;
    public double value;

    public int compareTo(DataPoint p2){
        if(this.value < p2.value)
             return -1;
        if(this.value == p2.value)
             return 0;
        if(this.value > p2.value)
             return 1;
    }
}

然后建立这些 DataPoint 对象的 List 并调用 Collections.sort(dataPointList);

dataPointList 然后包含按排序顺序排列的值。

【讨论】:

    【解决方案3】:

    好吧,您的排序需要这么长时间的原因是因为您没有使用有效的排序算法。通常,当您对大量数据/记录/等进行排序时,您希望确定最适合您现有的算法或解决方案。

    目前,您的算法似乎有 O(N^2) 的运行时间,这通常很糟糕,这就是为什么它是 O(N^2)

    //The outer-loop traverses through the "N" indexes of the array.
    for(int i=0; i<sArr.length;i++) {
    
            //The inner-loop also traverses through the "N" indexes of the array
            for(int j= i+1; j<sArr.length;j++){
    
                //Most comparisons are able to be done in O(1)
                if(sArr[i].split("\\s")[1].compareToIgnoreCase(sArr[j].split("\\s")[1])<0){
    
                    //Assignments are done in O(1)
                    temp= sArr[j];
                    sArr[j]= sArr[i];
                    sArr[i]=temp;
                }
            }
    

    所以,当 N 变得足够大时,我们真正关心的是运行时间。随着 N 变大,我们将得到结果:O(N) * O(N) = O(N^2) 这是因为我们正在查看最坏情况。最好的情况是所有内容都已排序,我们所要做的就是查看每个元素,这会导致 O(N) 运行时。

    如果您查看像 Quicksort(或查看 Mergesort)这样的算法,您肯定会看到很大的改进,因为最坏情况和最佳情况(通常)的运行时间为 O(n log n),这比当前方法快得多。

    我鼓励您考虑实现更好/更有效的排序算法。

    我相信 Java 在他们的 Arrays 库中实现了归并排序。 Arrays.sort()

    【讨论】:

      【解决方案4】:

      您可能想要使用从头开始排序的TreeSet。我从 laubed 借了一小部分 DataPoint 课程。

      import java.util.TreeSet;
      
      public class Main {
          public static void main(String[] args) {
              TreeSet<DataPoint> set = new TreeSet<DataPoint>();
              String[] splitS;
              for (String s : YOUR_LIST) {
                  splitS = s.split(" ");
                  set.add(new DataPoint(splitS[0], Double.parseDouble(splitS[1])));
              }
              DataPoint[] sortedArray = new DataPoint[set.size()];
              set.toArray(sortedArray); //but to be honest there is no reason for using array at this point
          }
      }
      
      class DataPoint implements Comparable<DataPoint> {
          public String key;
          public double value;
      
          public DataPoint(String key, double value) {
              this.key = key;
              this.value = value;
          }
      
          public int compareTo(DataPoint p){
              return Double.compare(value, p.value);
          }
      }
      

      【讨论】:

        【解决方案5】:

        代码似乎有两个问题。第一个是这样的:

        String str="";
        for(String s: mylist) {
            str+=s+",";
        }
        String[] sArr = str.split(",");
        

        在这里,您显然无缘无故地加入了一个字符串集合,然后将其再次分解为一个数组。使用字符串连接(+ 运算符)这一事实使情况变得更糟。

        在 Java 中,字符串是不可变的对象。这意味着每个看起来像是在更改字符串对象的操作实际上都是在创建一个新对象。每次您执行str+=s+"," 时,您都会创建新对象。重复上千次是非常低效的。

        当您需要像这样编写String 时,您应该改用StringBuilder。不过,在这种情况下,我认为根本没有必要。

        如果我正确理解了您的代码,那么mylist 列表似乎已经包含您的记录,格式如下:

        ["A 0.53", "B 0.56", ...]
        

        如果是这种情况,可以直接对mylist进行排序。

        从这里开始,我将假设mylistList&lt;String&gt;。如果不是这种情况并且mylist 确实是String[],则只需使用Arrays.sort(mylist, comparator) 而不是mylist.sort(comparator)

        首先,您需要一种从String 记录中提取Double 值的方法,因为我想您正在尝试按double 而不是String 的数字进行比较。

        static Double doubleFromRecord(String record) {
            return Double.valueOf(record.split("\\s")[1]);
        }
        

        所以,doubleFromRecord("A 0.53") 返回 0.53 作为 Double

        现在您只需在 mylist 上直接调用 sort 并传递一个 Comparator 即可比较来自不同元素的数字:

        mylist.sort((r1, r2) -> doubleFromRecord(r1).compareTo(doubleFromRecord(r2)));
        

        并且mylist会被排序

        比较器:

        (r1, r2) -> doubleFromRecord(r1).compareTo(doubleFromRecord(r2))
        

        只需从mylist 中获取两个元素,并返回它们的数字部分之间的比较结果。

        如果可以的话,我建议你为你的记录创建一个类,例如:

        class Record {
            final String label;
            final double value;
        
            Record(String label, double value) {
                this.label = label;
                this.value = value;
            }
         }
        

        使用List&lt;Record&gt; 而不是List&lt;String&gt;。所以你可以轻松做到:

        myRecordList.sort((r1, r2) -> Double.compare(r1.value, r2.value));
        

        【讨论】:

          猜你喜欢
          • 2018-11-30
          • 2021-10-10
          • 2022-01-20
          • 2021-12-20
          • 1970-01-01
          • 2015-02-09
          • 2018-06-17
          相关资源
          最近更新 更多