Java中的String和long变量在性能方面的比较

4

正如我在其他问题中所读到的,int比long具有更好的性能,比string稍微更好。

假设我有一个大于int的数字,并且我想将其用于与类似数字的比较:在这种情况下,哪种类型的变量具有更好的性能?long还是string?

例如,比较111.222.333.444和555.666.777.888:

long x = 111222333444;
long y = 555666777888;
if(x == y){ /*code*/ }

VS

string x = "111222333444";
string y = "555666777888";
if(x.equals(y)){ /*code*/ }

哪种情况具有最佳性能?差异显着吗?


当涉及到性能问题时,唯一的真正答案是对其进行测试。 - David
如果你要测试它,请确保使用一个好的微基准测试工具(比如,例如JMH——虽然还有很多其他好的工具)。还要注意这些测试是不等价的。字符串中的前导零将产生不同的结果:例如1与"01"。 - Ted Hopp
into和long之间的区别几乎可以忽略不计。在32位平台上可能会看到差异,但是考虑到JVM和JIT代码的开销,通常不值得过于关注。 - scooter me fecit
字符串是一系列字节,而不是单个32位或64位的数量。整型和长整型的比较应该比比较一系列字节或单词更有效。 - scooter me fecit
数值或布尔数据类型总是比其他用户定义的数据类型(包括字符串)更快。然而,char数据类型也非常快,因为它存储int值并返回该数字的等效ASCII字符。 - Rahul Raina
2个回答

13

正如我在另一个问题中所读到的那样,int的性能比long要好得多......

嗯,你所读到的可能是错的。或者更可能的是,你从你所读到的中理解错了。

确实,在某些机器上,对long进行算术运算可能比类似的int操作需要更长的时间。但在现代计算机上,这两种类型进行算术运算基本没有区别(就算有,也只会相差1或2个时钟周期,即纳秒级别)。

因此,“比较好”是夸大其词了。

... 而且比字符串的性能稍微好一点。

这也是错误的。事实上,对int数值的操作要比对String对象的操作快得多。例如,请考虑以下内容:

String x = "111222333444";
String y = "555666777888";
if (x.equals(y)) { /*code*/ }
equals方法会执行以下操作:
  1. 检测x == y是否成立,如果成立则返回true
  2. 检测y是否为字符串类型,如果不是则返回false
  3. 检测两个字符串的长度是否相等,如果不相等则返回false
  4. 循环遍历字符串中的字符,比较相同位置的字符,如果它们不相等则返回false
  5. 如果循环完成时没有找到不相等的字符,则返回true

另外需要注意的是调用该方法本身也会有额外开销(过大而无法内联)。

我估计这至少需要20到30条机器指令,如果两个字符串相等或者以相同字符开头将需要更多。

然而,这里有一个更大的问题。

除非你在处理数百万个数字,否则优化代码可能是浪费时间。我的建议是:

  • 除非必须,否则不要进行优化。
  • 即使必须优化,也不要轻易尝试。
  • 如果必须优化(例如存在实际且明确的性能问题),则应该通过编写基准测试并运行 >>真实<< 的数据来科学地确定代码的瓶颈。然后只优化代码中的这些部分。

1 - 我不是指令级性能方面的专家,但是这个文档似乎表明,Intel的CMP指令需要1、2或3个时钟周期。


4

long会更快一些——在64位平台上,它的速度将与int相当。但是,除非它在一个非常紧密的循环中使用,否则对于应用程序整体来说,它可能不会有任何显着的差异。微小优化的第一条规则是:不要进行微小优化。

我编写了一个快速的JMH基准测试来测试int、long和String的比较,在我的笔记本电脑上得出了以下结果:

EqualityComparisons.cInt     avgt  100  0.004 ±  0.001  us/op
EqualityComparisons.cLong    avgt  100  0.006 ±  0.001  us/op
EqualityComparisons.cString  avgt  100  0.011 ±  0.001  us/op

基准测试中的相关部分是:

@Setup
public void setup() {
  one = (int) System.nanoTime();
  two = QuickRand.next(one);
  oneS = String.format("%11d", one);
  twoS = String.format("%11d", two);
  oneL = one;
  twoL = two;
}

@Benchmark
public boolean cInt() { return one == two; }

@Benchmark
public boolean cLong() { return oneL == twoL; }

@Benchmark
public boolean cString() { return oneS.equals(twoS); }

需要注意的几点:

  • long和int在速度上差不多——误差在可接受范围内
  • 字符串比long和int慢大约2.75倍
  • 即使是那些较慢的字符串每次比较仍然只需要约0.011微秒,也就是说您可以每秒做大约1亿次比较。换句话说,这并不重要。

我对任何(表面上)声称每次 intlong 比较需要 0.04 到 0.06 微秒的基准测试都表示怀疑。 - Stephen C
@StephenC,顺便说一下,我的基准测试显示大约为0.004-0.006微秒——我认为您误读并且掉了一个零(在我的项目符号中的String位也是我打错了——我会进行编辑)。 - yshavit
我是根据你在项目符号中提到的“String”数字进行推断的。但即便如此,2.75倍的因子与我的直觉相差约一个数量级。我本以为差距会更大。 - Stephen C
@StephenC 我也是,说实话。我不知道,分支预测和其他优化?虽然我不是 JMH 专家,但我发布了基准测试,所以欢迎任何可操作的反馈。 - yshavit
@StephenC 我的基准测试中存在一个问题,即比较始终具有相同的结果--返回true。稍后我将使用修改后的基准测试重新运行它,其中一些比较返回true,而其他比较返回false。 - yshavit
@StephenC 我重新运行了基准测试,参数化使得有时两个数字/整数相等,有时不相等。我还延长了基准测试的时间,虽然所有数字都下降了一点,但比率与上面差不多。当数字不相等时,比率实际上更接近(这是有道理的,因为用字符串确定不等可以短路,意味着它们更好--也就是相对较少糟糕)。 - yshavit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接