时间复杂度:google.common.base.Joiner vs String concatenation

时间:2017-11-20 18:07:31

标签: java time-complexity guava complexity-theory

我知道在循环中使用+ = on字符串需要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吗?

4 个答案:

答案 0 :(得分:2)

我实际上对此也非常感兴趣,主要是因为我们很快就会转到java-9而我正在考虑将Joiner替换为普通+,因为它们现在是{{1}调用,似乎他们更快。这是我的测试(请注意,即使是普通的invokedynamic方法也比StringBuilder快7倍):

Joiner

结果如下:

    @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();
    }

    @State(Scope.Thread)
    public static class ThreadState {

        public List<String> list;

        @Setup(Level.Iteration)
        public void setUp() {
            int howMany = ThreadLocalRandom.current().nextInt(100, 10_000);
            list = new ArrayList<>(howMany);

            for (int i = 0; i < list.size(); ++i) {
                list.add("" + i);
            }
        }

        @TearDown(Level.Iteration)
        public void tearDown() {
            list = null;
        }

    }

    @Benchmark
    @Fork(1)
    public String guavaJoiner(ThreadState state) {
        return Joiner.on(',').join(state.list);
    }

    @Benchmark
    @Fork(jvmArgsAppend = "-Djava.lang.invoke.stringConcat=BC_SB")
    public String java9StringBuilder(ThreadState state) {
        String s = "";
        List<String> list = state.list;
        int size = list.size();
        for (int i = 0; i < size; ++i) {
            s += list.get(i) + ",";
        }
        return s;
    }

    @Benchmark
    @Fork(1)
    public String java9Default(ThreadState state) {
        String s = "";
        List<String> list = state.list;
        int size = list.size();
        for (int i = 0; i < size; ++i) {
            s += list.get(i) + ",";
        }
        return s;
    }

答案 1 :(得分:0)

由于JVM运行时优化,不可能声明Guava的Joiner能够100%保证更有效,在某些情况下,简单连接可以更快地工作。

这就是说,更喜欢Joiner(或类似于利用StringBuilder的结构)来连接集合,因为它的可读性和性能通常更好。

答案 2 :(得分:0)

在一种非常迂腐的意义上,如果您的输入上限是固定大小,那么对该输入执行的任何操作都是有效的恒定时间,但这会错过这种分析的目的。如果您对时间复杂度感兴趣,请检查代码在asymptotic情况下的行为,而不是单个特定输入的行为。

即使你将列表的大小限制为20个元素,你仍然在做O(n ^ 2)“工作”以连接元素。与使用StringBuilder或更高级别工具(例如设计)的Joiner或更高级别工具相比,使其比重复连接更有效。 Joiner只需做O(n)“工作”就可以构造你需要的字符串。

简单地说,永远不会做出理由:

 for(String str : strList) appendStr += str + ","; 

而不是:

Joiner.on(',').join(strList);

答案 3 :(得分:0)

我找到了一篇很棒的博客文章,详细介绍了每种串联技术的性能java-string-concatenation-which-way-is-best

注:串联性能随否而变化。要连接的字符串。例如,要连接1-10个字符串,这些技术最有效-StringBuilder,StringBuffer和Plus运算符。为了连接100多个字符串-Guava Joiner,apache的stringsUtils库也很好用。

请浏览上述博客。它确实很好地解释了各种串联技术的性能。

谢谢。