时间复杂度:google.common.base.Joiner vs 字符串连接
time complexity: google.common.base.Joiner vs String concatenation
我知道在循环中对字符串使用 += 需要 O(n^2) 时间,其中 n 是循环数。但是如果循环会运行最多20次。这会将时间复杂度更改为 O(1) 吗?例如,
List<String> strList = new ArrayList<>();
//some operations to add string to strList
for(String str : strList) appendStr += str + ",";
我知道 strList 的大小永远不会超过 20。而且 strList 中的每个字符串都将少于 20 个字符。
如果这种情况下的字符串连接仍然具有 O(n^2) 时间复杂度,如果我希望我的算法具有更好的时间复杂度,那么使用 google.common.base.Joiner
会更好吗?
由于 JVM 运行时优化,不可能说 Guava 的 Joiner
会 100% 更有效地工作,在某些情况下,普通连接会工作得更快。
也就是说,更喜欢 Joiner
(或在幕后利用 StringBuilder
的类似构造)来连接集合,因为它的可读性和性能通常更好。
从非常迂腐的意义上讲,是的,如果您的输入被限制在固定大小,那么对该输入执行的任何操作实际上都是恒定时间的,但是这与此类分析的目的背道而驰。如果您对其时间复杂度感兴趣,而不是它对单个特定输入的行为方式,请检查您的代码在 asymptotic 情况下的行为方式。
即使您将列表的大小限制为 20 个元素,您仍然需要 O(n^2) "work" 来连接元素。与使用 StringBuilder
或更高级别的工具(例如 Joiner
相比,这些工具 设计 比重复连接更有效。 Joiner
只需执行 O(n) "work" 即可构造您需要的字符串。
简而言之,从来没有这样做的理由:
for(String str : strList) appendStr += str + ",";
而不是:
Joiner.on(',').join(strList);
我已经完全删除了我之前的答案,因为我的测试存在严重缺陷。以下是一些更新的结果和代码:
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
public class DifferentConcats {
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder().include(DifferentConcats.class.getSimpleName())
.verbosity(VerboseMode.EXTRA)
.build();
new Runner(opt).run();
}
@Param(value = {"1", "10", "100", "1000", "10000"})
private int howMany;
private static final Joiner JOINER = Joiner.on(",");
@Benchmark
@Fork(3)
public String guavaJoiner() {
List<String> list = new ArrayList<>(howMany);
for (int i = 0; i < howMany; ++i) {
list.add("" + i);
}
return JOINER.join(list);
}
@Benchmark
@Fork(3)
public String java9Default() {
List<String> list = new ArrayList<>(howMany);
for (int i = 0; i < howMany; ++i) {
list.add("" + i);
}
String result = "";
for (String s : list) {
result += s;
}
return result;
}
}
结果:
Benchmark (howMany) Mode Cnt Score Error Units
DifferentConcats.guavaJoiner 1 avgt 15 62.582 ± 0.756 ns/op
DifferentConcats.java9Default 1 avgt 15 47.209 ± 0.708 ns/op
DifferentConcats.guavaJoiner 10 avgt 15 430.310 ± 4.690 ns/op
DifferentConcats.java9Default 10 avgt 15 377.203 ± 4.071 ns/op
DifferentConcats.guavaJoiner 100 avgt 15 4115.152 ± 38.505 ns/op
DifferentConcats.java9Default 100 avgt 15 4659.620 ± 182.488 ns/op
DifferentConcats.guavaJoiner 1000 avgt 15 43917.367 ± 360.601 ns/op
DifferentConcats.java9Default 1000 avgt 15 362959.115 ± 6604.020 ns/op
DifferentConcats.guavaJoiner 10000 avgt 15 435289.491 ± 5391.097 ns/op
DifferentConcats.java9Default 10000 avgt 15 47132980.336 ± 1152934.498 ns/op
TL;DR
另一个被接受的答案是绝对正确的。
我发现了一篇很棒的博客post 详细解释了每种串联技术的性能java-string-concatenation-which-way-is-best
注意:串联性能因编号而异。要连接的字符串。例如 - 要连接 1-10 个字符串,这些技术最有效 - StringBuilder、StringBuffer 和 Plus Operator。连接 100 个字符串 - Guava Joiner,apache 的 stringsUtils 库也很好用。
请浏览以上博客。真的很好的诠释了各种concatenation Techniques的性能。
谢谢。
我知道在循环中对字符串使用 += 需要 O(n^2) 时间,其中 n 是循环数。但是如果循环会运行最多20次。这会将时间复杂度更改为 O(1) 吗?例如,
List<String> strList = new ArrayList<>();
//some operations to add string to strList
for(String str : strList) appendStr += str + ",";
我知道 strList 的大小永远不会超过 20。而且 strList 中的每个字符串都将少于 20 个字符。
如果这种情况下的字符串连接仍然具有 O(n^2) 时间复杂度,如果我希望我的算法具有更好的时间复杂度,那么使用 google.common.base.Joiner
会更好吗?
由于 JVM 运行时优化,不可能说 Guava 的 Joiner
会 100% 更有效地工作,在某些情况下,普通连接会工作得更快。
也就是说,更喜欢 Joiner
(或在幕后利用 StringBuilder
的类似构造)来连接集合,因为它的可读性和性能通常更好。
从非常迂腐的意义上讲,是的,如果您的输入被限制在固定大小,那么对该输入执行的任何操作实际上都是恒定时间的,但是这与此类分析的目的背道而驰。如果您对其时间复杂度感兴趣,而不是它对单个特定输入的行为方式,请检查您的代码在 asymptotic 情况下的行为方式。
即使您将列表的大小限制为 20 个元素,您仍然需要 O(n^2) "work" 来连接元素。与使用 StringBuilder
或更高级别的工具(例如 Joiner
相比,这些工具 设计 比重复连接更有效。 Joiner
只需执行 O(n) "work" 即可构造您需要的字符串。
简而言之,从来没有这样做的理由:
for(String str : strList) appendStr += str + ",";
而不是:
Joiner.on(',').join(strList);
我已经完全删除了我之前的答案,因为我的测试存在严重缺陷。以下是一些更新的结果和代码:
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
public class DifferentConcats {
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder().include(DifferentConcats.class.getSimpleName())
.verbosity(VerboseMode.EXTRA)
.build();
new Runner(opt).run();
}
@Param(value = {"1", "10", "100", "1000", "10000"})
private int howMany;
private static final Joiner JOINER = Joiner.on(",");
@Benchmark
@Fork(3)
public String guavaJoiner() {
List<String> list = new ArrayList<>(howMany);
for (int i = 0; i < howMany; ++i) {
list.add("" + i);
}
return JOINER.join(list);
}
@Benchmark
@Fork(3)
public String java9Default() {
List<String> list = new ArrayList<>(howMany);
for (int i = 0; i < howMany; ++i) {
list.add("" + i);
}
String result = "";
for (String s : list) {
result += s;
}
return result;
}
}
结果:
Benchmark (howMany) Mode Cnt Score Error Units
DifferentConcats.guavaJoiner 1 avgt 15 62.582 ± 0.756 ns/op
DifferentConcats.java9Default 1 avgt 15 47.209 ± 0.708 ns/op
DifferentConcats.guavaJoiner 10 avgt 15 430.310 ± 4.690 ns/op
DifferentConcats.java9Default 10 avgt 15 377.203 ± 4.071 ns/op
DifferentConcats.guavaJoiner 100 avgt 15 4115.152 ± 38.505 ns/op
DifferentConcats.java9Default 100 avgt 15 4659.620 ± 182.488 ns/op
DifferentConcats.guavaJoiner 1000 avgt 15 43917.367 ± 360.601 ns/op
DifferentConcats.java9Default 1000 avgt 15 362959.115 ± 6604.020 ns/op
DifferentConcats.guavaJoiner 10000 avgt 15 435289.491 ± 5391.097 ns/op
DifferentConcats.java9Default 10000 avgt 15 47132980.336 ± 1152934.498 ns/op
TL;DR
另一个被接受的答案是绝对正确的。
我发现了一篇很棒的博客post 详细解释了每种串联技术的性能java-string-concatenation-which-way-is-best
注意:串联性能因编号而异。要连接的字符串。例如 - 要连接 1-10 个字符串,这些技术最有效 - StringBuilder、StringBuffer 和 Plus Operator。连接 100 个字符串 - Guava Joiner,apache 的 stringsUtils 库也很好用。
请浏览以上博客。真的很好的诠释了各种concatenation Techniques的性能。
谢谢。