获取字符串的子字符串是一种非常常见的字符串操作操作,但我听说Java和.NET平台之间的性能/实现可能存在很大差异。具体来说,我听说在Java中,java.lang.String
为substring
提供常量时间操作,但在.NET中,System.String
提供线性性能Substring
。
这些确实如此吗?可以在文档/源代码等中确认吗?此实现是特定的,还是由语言和/或平台指定的?每种方法的优缺点是什么?一个人从一个平台迁移到另一个平台应该寻找什么来避免陷入任何性能陷阱?
答案 0 :(得分:11)
在.NET中,Substring
是O(n)而不是Java的O(1)。这是因为在.NET中,String对象包含所有实际的字符数据本身 1 - 因此获取子字符串涉及复制新子字符串中的所有数据。在Java中,substring
可以创建一个引用原始char数组的新对象,具有不同的起始索引和长度。
每种方法都有利弊:
char[]
被垃圾收集的情况。我相信在某些情况下,内部也可以很容易互操作。我的strings article中有更多细节。
至于避免性能缺陷的一般问题,我认为我应该准备好剪切和粘贴的固定答案:确保架构高效,并以最易读的方式实现它能够。衡量绩效,并优化您遇到瓶颈的地方。
1 顺便说一句,这使得string
非常特殊 - 它是唯一的非数组类型,其内存占用量在同一个CLR中因实例而异。
2 对于小字符串,这是一个很大的胜利。 一个对象的所有开销已经足够糟糕了,但是当涉及到额外的数组时,单字符字符串在Java中可能需要大约36个字节。 (这是一个“空中手指”号码 - 我不记得确切的对象开销。它还取决于你正在使用的VM。)
答案 1 :(得分:2)
使用反射器这是从子串(Int32,Int32)
获得的[SecuritySafeCritical, TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")]
public string Substring(int startIndex, int length)
{
return this.InternalSubStringWithChecks(startIndex, length, false);
}
如果你继续进行最后一次通话,那么
internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount)
使用指针复制字符。 完整的代码实际上看起来很大,但在运行它并对其进行基准测试之前,你不会看到它有多快或多慢。
答案 2 :(得分:1)
根据这不是真的: C# Substring
答案 3 :(得分:0)
这实际上取决于您的工作量。如果您正在循环并执行大量子字符串调用,那么您可能会遇到问题。对于你所指的SO帖子,我怀疑它会成为一个问题。然而,凭借这种态度,你总能在“千纸减少”的情况下结束。在您提到的SO帖子中,我们有以下内容:
String after = before.Substring(0, 1).ToUpper() + before.Substring(1);
假设编译器没有进行一些疯狂的优化,这将创建至少四个新字符串(2 Substring
个调用,ToUpper
调用和连接)。子串完全按照您的预期实现(字符串复制),但上面分配的三个字符串很快就会变成垃圾。做很多事情会造成不必要的记忆压力。我说“不必要”,因为你可以提出一个更经济的解决方案,只需要更多的时间投资。
最后,探查器是你最好的朋友:)