为什么Java中的字符串比较(CompareTo)比C#中更快

本文关键字:CompareTo Java 字符串 比较 为什么 | 更新日期: 2023-09-27 18:02:28

编辑3:

使用

StringComparer comparer1 = StringComparer.Ordinal;

而不是

IComparable v
IComparable w
comparer1.Compare(v, w)

解决了运行时问题。


我在Java和C#中做了一些排序算法的基准测试(例如Quicksort、Mergesort(。

我使用了Java 7和。NET Framework 4.5来实现和执行我的算法。结果表明,所有算法都可以使用Java实现更好的运行时间。

Quicksort:的一些运行时示例

C#

  1. n=1000000 4433毫秒
  2. n=2000000 10047毫秒

Java

  1. n=1000000 1311毫秒
  2. n=2000000 3164毫秒

然后,我使用评测工具进行了测量:C#使用75%的运行时进行字符串比较(即CompareTo(,而Java只使用2%的运行时来进行比较。

为什么字符串比较在C#中如此昂贵而在Java中却如此昂贵?

编辑:我还测试了C#排序函数Arrays.sort(INPUT(,当n=1000000时,它可以达到大约3000ms,所以我认为代码不是问题所在。但无论如何:

这是快速分拣的代码:

public class Quicksort {
public static void sort(IComparable[] a) {
    sort(a, 0, a.Length - 1);
}
private static void sort(IComparable[] a, int lo, int hi) { 
    if (hi <= lo) return;
    int j = partition(a, lo, hi);
    sort(a, lo, j-1);
    sort(a, j+1, hi);
}
private static int partition(IComparable[] a, int lo, int hi) {
    int i = lo;
    int j = hi + 1;
    IComparable v = a[lo];
    while (true) { 
        while (less(a[++i], v))
            if (i == hi) break;
        while (less(v, a[--j]))
            if (j == lo) break; 

        if (i >= j) break;
        exch(a, i, j);
    }
    exch(a, lo, j);
    return j;
}

public static IComparable select(IComparable[] a, int k) {
    if (k < 0 || k >= a.Length) {
        throw new Exception("Selected element out of bounds");
    }
    Rnd.Shuffle(a);
    int lo = 0, hi = a.Length - 1;
    while (hi > lo) {
        int i = partition(a, lo, hi);
        if      (i > k) hi = i - 1;
        else if (i < k) lo = i + 1;
        else return a[i];
    }
    return a[lo];
}

private static bool less(IComparable v, IComparable w) {
    return (v.CompareTo(w) < 0);
}
    
private static void exch(Object[] a, int i, int j) {
    Object swap = a[i];
    a[i] = a[j];
    a[j] = swap;
}
}

快速分拣的测量如下:

Stopwatch.Restart();
Quicksort.sort(stringArray);
Stopwatch.Stop();

第二版:有人想看看Java版本。这完全一样,我只是用Comparable代替IComparable,用Array.length代替Array。长度

为什么Java中的字符串比较(CompareTo)比C#中更快

所以我不认为代码是的问题

我不同意。在这两种情况下,你不是在比较相同的东西。诚然,您没有向我们展示如何生成字符串等,但Java和。NET执行不同的默认字符串比较。

来自Java的String.compareTo:

按字典顺序比较两个字符串。

以及来自。NET的String.CompareTo:

此方法使用当前区域性执行单词(区分大小写和区域性(比较。

毫不奇怪,这比纯粹的词典学比较要慢。

仅仅进行比较就很容易看出这一点。NET本身。。。

using System;
using System.Diagnostics;
using System.Text;
class Test
{
    static void Main()
    {
        string[] source = GenerateRandomStrings();
        string[] workingSpace = new string[source.Length];
        CopyAndSort(source, workingSpace);
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < 1000; i++)
        {
            CopyAndSort(source, workingSpace);
        }
        sw.Stop();
        Console.WriteLine("Elapsed time: {0}ms", 
                          (long) sw.Elapsed.TotalMilliseconds);
    }
    static string[] GenerateRandomStrings()
    {
        Random rng = new Random();
        string[] ret = new string[10000];
        for (int i = 0; i < ret.Length; i++)
        {
            ret[i] = GenerateRandomString(rng);
        }
        return ret;
    }
    static string GenerateRandomString(Random rng)
    {
        char[] chars = new char[30];
        for (int i = 0; i < chars.Length; i++)
        {
            chars[i] = (char) rng.Next('A', 'z' + 1);
        }
        return new string(chars);
    }
    static void CopyAndSort(string[] original, string[] workingSpace)
    {
        Array.Copy(original, 0, workingSpace, 0, original.Length);
        Array.Sort(workingSpace);
        // Array.Sort(workingSpace, StringComparer.Ordinal);
    }
}

您可以自己尝试,根据是否指定序号字符串比较器来更改CopyAndSort方法。至少在我的盒子上,使用顺序比较器的速度要快得多。

我不是100%确定,但我认为在C#中。Net平台可能会在默认情况下进行一些扩展检查,如正确跳过注释或空白unicode字符,而Java可能不会在缺省情况下进行这些检查。我认为Java的运行时执行更简单的字节2字节比较,但我在这里可能大错特错,因为我已经很久没有接触过在Java中使用编码的细节了,所以这纯粹是猜测。

另一件事是:这两个平台之间的默认比较行为可能存在一些差异。如果你只是使用默认值,而没有任何精确的设置,我猜你只是含蓄地要求不同的行为。

至少在。Net有许多字符串比较选项。您可能只是简单地使用了外观相似的函数,该函数实际上与Java的函数进行了不同的比较。你有没有尝试过像这样的详细过载http://msdn.microsoft.com/en-us/library/cc190416.aspx?请注意,这是一种static方法,使用方式略有不同:

var result1 = "mom".CompareTo("dad");
var result2 = string.Compare("mom", "dad", ...);

请调查ComparisonOptions和/或CultureInfo设置,并对它们进行调整,使整体行为尽可能与Java的行为相匹配。此外,如果有更多的重载,您可能还必须在Java端选择不同的重载。

此外,另一个区别可能是有些棘手的事实,即您正在测试的CompareTo方法是IComparable<T>接口的实现,该接口旨在交叉比较实现该接口的各种对象,而静态Compare方法旨在仅比较字符串。它们可能只是针对不同的事情进行了优化。如果它们之间有任何性能差异,我敢打赌静态Compare在字符串与字符串比较中会更快。