改善收藏。排序

时间:2011-12-25 13:13:04

标签: java

我正在使用

在ArrayList中排序100万个字符串(每个字符串50个字符)
final Comparator comparator= new Comparator<String>() {

     public int compare(String s1, String s2) {

    if (s2 == null || s1 == null)
         return 0;
    return s1.compareTo(s2);
     }
};

Collections.Sort(list,comparator);

平均时间为:1300毫升

如何加快速度?

3 个答案:

答案 0 :(得分:4)

如果您使用的是Java 6或更低版本,则可以通过切换到Java 7来获得加速。在Java 7中,他们将排序算法更改为TimSort,在某些情况下表现更好(特别是works well with partially sorted input) 。 Java 6及以下used MergeSort


但是我们假设您使用的是Java 6.我尝试了三个版本:

Collections.sort():重复运行您提供的比较器在我的机器上执行 3.0秒(包括读取1,000,000个随机生成的小写ascii字符串的输入)

基数排序:其他答案建议Radix sort。我尝试了以下代码(假设字符串的长度都相同,只有小写的ascii):

String [] A = list.toArray(new String[0]);

for(int i = stringLength - 1; i >=0; i--) {
  int[] buckets = new int[26];
  int[] starts = new int[26];
  for (int k = 0 ; k < A.length;k++) {
    buckets[A[k].charAt(i) - 'a']++;
  }
  for(int k = 1; k < buckets.length;k++) {
    starts[k] = buckets[k -1] + starts[k-1];
  }
  String [] temp = new String[A.length];
  for(int k = 0; k < A.length; k++) {
    temp[starts[A[k].charAt(i) - 'a']] = A[k];
    starts[A[k].charAt(i) - 'a']++;
  }    
  A = temp;
}

我的机器上需要 29.0秒才能完成。我不认为这是为这个问题实现基数排序的最佳方法 - 例如,如果你做了一个最重要的数字排序,那么你可以提前终止唯一的前缀。而使用就地排序也会有一些好处(关于这一点有一个很好的引用 - “The troubles with radix sort are in implementation, not in conception”)。我想写一个更好的基于基数排序的解决方案,这样做 - 如果我有时间,我会更新我的答案。

Bucket Sort:我还实施了Peter Lawrey的bucket sort解决方案的略微修改版本。这是代码:

Map<Integer, List<String>> buckets = new TreeMap<Integer,List<String>>();
for(String s : l) {
  int key = s.charAt(0) * 256 + s.charAt(1);
  List<String> list = buckets.get(key);
  if(list == null) buckets.put(key, list = new ArrayList<String>());
  list.add(s);
}
l.clear();
for(List<String> list: buckets.values()) {
    Collections.sort(list);
    l.addAll(list);
}

在我的机器上完成 2.5秒。我相信这次胜利来自于分区。


因此,如果切换到Java 7的TimSort对您没有帮助,那么我建议对数据进行分区(使用bucket sort之类的东西)。如果您需要更好的性能,那么您还可以多线程处理分区。

答案 1 :(得分:2)

你没有指定你使用的排序算法比其他算法更快(快速/合并与泡沫) 此外,如果您在多核/多处理器计算机上运行,​​您可以在多个线程之间划分排序(再次具体取决于排序算法,但here's示例)

答案 2 :(得分:2)

您可以对前两个字符使用基数排序。如果你前两个字符是独特的,你可以使用类似的东西。

List<String> strings = 
Map<Integer, List<String>> radixSort = 
for(String s: strings) {
  int key = (s.charAt(0) << 16) + s.charAt(1);
  List<String> list = radixSort.get(key);
  if(list == null) radixSort.put(key, list = new ArrayList<String>());
  list.add(s);
}
strings.clear();
for(List<String> list: new TreeMap<Integer, List<String>>(radixSort).values()) {
    Collections.sort(list);
    strings.addAll(list);
}