- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我尝试过这个例子来发现 StringBuffer、StringBuilder 和 String 的执行时间不同
经过尝试,我发现 StringBuffer 和 StringBuilder 花费的时间更少,因为它没有创建新对象。
作为字符串追加空字符串也不会创建任何对象,因此速度更快。
当我 append 一些字符串时,应该需要更多时间,因为创建对象需要时间。
当我对另一个字符串执行相同的 append 字符串模式时,这也花费了更多时间。在这种情况下,所有对象都已在字符串池中可用。为什么它花费的时间和以前一样?
public class StringComparation {
public static void main(String[] args) {
int N = 100000;
long time;
// String Buffer
StringBuffer sb = new StringBuffer();
time = System.currentTimeMillis();
for (int i = N; i --> 0 ;) {
sb.append("a");
}
System.out.println("String Buffer - " + (System.currentTimeMillis() - time));
// String Builder
StringBuilder sbr = new StringBuilder();
time = System.currentTimeMillis();
for (int i = N; i --> 0 ;) {
sbr.append("a");
}
System.out.println("String Builder - " + (System.currentTimeMillis() - time));
// String Without String pool value
String s2 = new String();
time = System.currentTimeMillis();
for (int i = N; i --> 0 ;) {
s2 = s2 + "";
}
System.out.println("String Without String pool value - "
+ (System.currentTimeMillis() - time));
// String With new String pool Object
String s = new String();
time = System.currentTimeMillis();
for (int i = N; i --> 0 ;) {
s = s + "a";
}
System.out.println("String With new String pool Object - "
+ (System.currentTimeMillis() - time));
// String With already available String pool Object
String s1 = new String();
time = System.currentTimeMillis();
for (int i = N; i --> 0 ;) {
s1 = s1 + "a";
}
System.out.println("String With already available String pool Object - "
+ (System.currentTimeMillis() - time));
}
}
输出:
String Buffer - 43
String Builder - 16
String Without String pool value - 64
String With new String pool Object - 12659
String With already available String pool Object - 14258
如有错误,请指正。
最佳答案
鉴于您的最后两个测试是相同的,因此您实际上只有四个测试。为了方便起见,我将它们重构为单独的方法并删除了基准测试代码,因为没有必要了解这里发生的情况。
public static void stringBuilderTest(int iterations) {
final StringBuilder sb = new StringBuilder();
for (int i = iterations; i-- > 0;) {
sb.append("a");
}
}
public static void stringBufferTest(int iterations) {
final StringBuffer sb = new StringBuffer();
for (int i = iterations; i-- > 0;) {
sb.append("a");
}
}
public static void emptyStringConcatTest(int iterations) {
String s = new String();
for (int i = iterations; i-- > 0;) {
s += "";
}
}
public static void nonEmptyStringConcatTest(int iterations) {
String s = new String();
for (int i = iterations; i-- > 0;) {
s += "a";
}
}
我们已经知道 StringBuilder 版本的代码是四个版本中最快的。 StringBuffer 版本速度较慢,因为它的所有操作都是同步的,这带来了 StringBuilder 没有的不可避免的开销,因为它不同步。
所以我们感兴趣的两个方法是 emptyStringConcatTest
和nonEmptyStringConcatTest
。如果我们检查 emptyStringConcatTest
编译版本的字节码,我们看到以下内容:
public static void emptyStringConcatTest(int);
flags: ACC_PUBLIC, ACC_STATIC
LineNumberTable:
line 27: 0
line 28: 8
line 29: 17
line 31: 40
Code:
stack=2, locals=3, args_size=1
0: new #14 // class java/lang/String
3: dup
4: invokespecial #15 // Method java/lang/String."<init>":()V
7: astore_1
8: iload_0
9: istore_2
10: iload_2
11: iinc 2, -1
14: ifle 40
17: new #7 // class java/lang/StringBuilder
20: dup
21: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
24: aload_1
25: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: ldc #16 // String
30: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
33: invokevirtual #17 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
36: astore_1
37: goto 10
40: return
LineNumberTable:
line 27: 0
line 28: 8
line 29: 17
line 31: 40
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 10
locals = [ class java/lang/String, int ]
frame_type = 250 /* chop */
offset_delta = 29
在底层,这两种方法几乎相同,唯一的区别是这一行:
空字符串:
28: ldc #9 // String
非空字符串(注意微小但重要的区别!):
28: ldc #9 // String a
关于字节码,首先要注意的是for
的结构。循环体:
10: iload_2
11: iinc 2, -1
14: ifle 40
17: new #7 // class java/lang/StringBuilder
20: dup
21: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
24: aload_1
25: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: ldc #16 // String
30: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
33: invokevirtual #17 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
36: astore_1
37: goto 10
我们实际上最终得到的是编译器优化
for (int i = iterations; i-- > 0;) {
s += "";
}
进入:
for (int i = iterations; i-- > 0;) {
s = new StringBuilder().append(s).append("").toString();
}
这不太好。我们在每一次迭代中实例化一个新的临时 StringBuilder 对象,其中有 100,000 次。这是很多对象。
您在 emptyStringConcatTest
之间看到的差异和nonEmptyStringConcatTest
如果我们检查StringBuilder#append(String)
的源代码可以进一步解释:
public StringBuilder append(String str) {
super.append(str);
return this;
}
StringBuilder的父类(super class)是AbstractStringBuilder,我们来看看它的append(String)
的实现:
public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
if (len == 0) return this;
int newCount = count + len;
if (newCount > value.length)
expandCapacity(newCount);
str.getChars(0, len, value, count);
count = newCount;
return this;
}
您会在这里注意到,如果参数的长度 str
为零,该方法只是返回而不执行任何进一步的操作,这使得在空字符串的情况下速度相当快。
非空字符串参数触发支持的边界检查 char[]
,可能会导致其大小调整为 expandCapacity(int)
,它将原始数组复制到一个新的、更大的数组中(请注意,StringBuilder 中的后备数组不是 final
- 它可以重新分配!)。完成后,我们调用 String#getChars(int, int, char[], int)
,它执行更多数组复制。数组复制的确切实现隐藏在 native 代码中,因此我不会四处寻找它们。
更复杂的是,我们创建然后丢弃的对象的绝对数量可能足以触发 JVM 垃圾收集器的运行,这会带来进一步的开销。
总而言之;相当于 nonEmptyStringConcatTest
的性能大幅下降很大程度上取决于编译器所做的糟糕的“优化”。通过不在循环内进行直接串联来避免这种情况。
关于java - 即使字符串池中已有可用的对象,字符串追加也会花费更多时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25305933/
如何检测当前网络中计算机是否已连接/可用。 当然,它有多种用途,但我主要关心的是我的应用程序使用位于特定计算机中的资源,如果这些资源不可用,它甚至不会尝试连接,而是使用本地资源。 最佳答案 您可以尝试
雅虎建议load scripts at the bottom of an HTML pages出于性能原因。我使用遵守规则的 HTML5 Boilerplate。 这种方法的问题是 jQuery 也是
我有一个场景,我需要只向管理员而不是普通用户展示一个组件。 说, // This component should be rendered for public users.
我有一个数组,比方说 ["a","b","c"],我想把它变成一个对象,它以数组值作为键和一个默认值我可以设置。因此,如果默认值为 true,我希望我的输出为 {a:true, b:true, c:t
我假设在函数中创建一个新的 PS-Drive 会使该驱动器只能在该函数中访问。 如何通过从该函数可访问的 MAIN 调用函数来创建 PS 驱动器? $temproraryPSDriveName =
您会推荐哪些 .net 控制库作为企业必备的? 可以免费/非免费 至少应该包含一个真正强大的Datagrid Ajax 功能 有没有可用的比较? 最佳答案 我用过 Telerik ASP.NET控制之
假设我有一些像这样的 html: Hello World Javascript代码: var fooBar = document.getElementById('fooBar'); fooBar.
开始实现 camera plugin 时出现以下错误在我的 flutter 应用程序上: [VERBOSE-2:dart_error.cc(16)] Unhandled exception:
我想知道应该在哪里正确放置 countA,因为我希望将计数添加到所做的每个按钮中。开头的“count”变量用于制作新按钮,“countA”是我试图声明的计数,但它无法编译。这是我的代码片段:我的问题再
在python帮助文档中我经常看到带有函数名和命名参数的签名行,然后你会看到**kwarg(关键字参数)。 list? 打印到帮助文档字符串: Init signature: list(self, /
我是 UWP 的新手,想知道“Flyout”、“ContextFlyout”和“Popup”之间的区别。另外,我想知道“AppBar”和“CommandBar”之间的区别。我相信这些是容纳菜单或菜单项
我想显示可用的 Wifi 设备列表。这是我的代码,我不明白这里有什么错误: wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE); i
这是我的代码: Random = function(name) { this.name = name; this.addSomething = function(a, b) {
我希望我的按钮持续动画直到用户触摸它,这是代码 func animate() { UIView.animateWithDuration(1, animations: { () -> Void
我正在为我正在参加的类(class)制作一个 HTTP 服务器作为项目,一周来我一直在尝试寻找解决方案。我有一个 DataInputStream,我需要等待客户端向我发送 http 请求,由于连接保持
我想创建一个 bash 脚本来检查我的 Linux 服务器中的哪些端口已关闭且未在特定范围(端口范围 (3000-3010))中使用。 打印输出只需1个端口,如果将输出保存为变量或保存在同一个文件中,
我写了一个我认为是最小的自定义设备来测试我对 QOM 和 QEMU 的总体理解。以下是省略注释的相关代码。不幸的是,当我启动 guest 并将我的设备名称作为命令行参数传递时,它无法找到我的设备并退出
调用Opencv函数时,通常会提供一个常量给函数调用,如: im_hsv = cv2.cvtColor(im, cv2.COLOR_BGR2HSV) 虽然我找不到对所有可用常量的引用。 Opencv
C++ 于 1998 年正式标准化,但我们能在多久以前找到一个名为 string 的类,它看起来像 C++2003 中的 std::string预标准 C++ 实现? 我问是因为 CString 作为
是否有一个官方的(或可能没有)git 命令列表,--porcelain 选项可用?或者我应该在 the porcelain commands list 中手动查看它们中的每一个吗? ? 我已经设法用谷
我是一名优秀的程序员,十分优秀!