【问题标题】:Performance (JAVA) ~ String concatenation in a loop with prepending and appending性能(JAVA)~ 循环中的字符串连接,带有前置和附加
【发布时间】:2017-04-19 08:05:46
【问题描述】:

我遇到了性能问题。有没有人有更快/更好的解决方案来执行以下操作:

    String main = "";
    for (String proposition : propositions) {
        if (main.length() == 0) {
            main = proposition;
        } else {
            main = "|(" + proposition + "," + main + ")";
        }
    }

我知道 concat 和 stringbuilder 更快,但我不知道如何使用这些方法。因为下面这行代码:

main = "|(" + proposition + "," + main + ")";

提前致谢!

【问题讨论】:

  • 你想达到什么目的?您的预期结果是什么?
  • @AndrewTobilko 他想要一种更快的方法来提高性能。
  • 您是否尝试过插入 StringBuilder? docs.oracle.com/javase/7/docs/api/java/lang/… 或在迭代之前反转propositions,那是否只允许您追加?
  • proposition.length() 可以为零吗?向后循环可能更容易使用StringBuilder
  • @lmiguelvargasf:赞成者可能会赞成,因为转换此代码以使用更有效的字符串构建形式实际上并非易事,因为混合了前置和附加。

标签: java string performance loops concatenation


【解决方案1】:

所以据我所知,这里有 3 个问题:

  1. 值主要附加到字符串中。
  2. 为每个值附加一个字符。
  3. 如果只存在一个值,则不应附加或前置任何值。
  4. 对于 2 个或更多项目,第 0 个项目的处理方式不同:

0:""

1:"A"

2:"|(B,A)"

3:"|(C,|(B,A))"

做一些改变可以更快:

  1. 反转算法,这意味着大部分工作都涉及追加,允许您使用 StringBuilders。
  2. 计算关闭) 的次数并在循环结束后追加。
  3. 列表中 0 或 1 项的特殊情况。

通过这些更改,算法应该能够使用 StringBuilder 并且速度更快。

尝试一种算法:

int length = propositions.size();
if (length == 0) {
    main = "";
} else {
    StringBuilder sb = new StringBuilder();
    int nestingDepth = 0;
    // Reverse loop, ignoring 0th element due to special case
    for (int i = length - 1; i > 0; i--) {
        sb.append("|(").append(propositions.get(i)).append(',');
        nestingDepth++;
    }
    // Append last element due to special casing
    sb.append(propositions.get(0));
    for (int i = 0; i < nestingDepth; i++) {
        sb.append(')');
    }

    main = sb.toString();
}

相信这应该会产生正确的结果,但它应该给出正确的想法。

【讨论】:

  • 他不需要逆算法。 StringBuilder.insert() 存在。
  • @EJP:但是使用insert 会失败,因为您不会对二次运行时做任何事情。
  • @EJP 插入需要移动所有先前的输入,它可能涉及与原始代码一样多的内存改组。
  • insert 可能需要移动输入,但仍然比创建所有这些字符串的原始速度更快
  • 另一种选择是使用ArrayDeque,它是一个循环结构,在开头插入的速度与结尾一样快。最后你可以加入所有的字符串。
【解决方案2】:

问题是您在进行时要预先添加和附加到字符串。 String 和 StringBuilder 不能很好地处理这个问题(并给出二次性能)。但是您可以使用支持在开始和结束时插入的出队来存储所有片段。最后你可以加入出队中的位。

ArrayDeque bits = new ArrayDeque();
for (String proposition : propositions) {
    if (bits.size() == 0) {
        bits.push(proposition);
    } else {
        // Add prefix
        main.offerFirst("|(" + proposition + "," );
        // Add suffix
        main.push(")");
    }
}
StringBuilder sb = new StringBuilder();
for( String s : bits) {
   sb.append(s);
}
main = sb.toString();

【讨论】:

  • 我要试试这个方法,谢谢大家的回复。
【解决方案3】:

假设这是一个propositions 的数组,您可以先将数组中String(s) 的长度相加。为您的其他字符添加 4,并减去 4,因为您没有在第一个元素上使用这些分隔符。这应该是您的输出的 完美 大小(这是可选的,因为 StringBuilder 是动态调整大小的)。接下来,构造一个StringBuilder。添加第一个元素。所有后续元素都遵循相同的模式,因此使用传统的for 简化了循环。类似的,

int len = Stream.of(propositions).mapToInt(s -> s.length() + 4).sum() - 4;
StringBuilder sb = new StringBuilder(len); // <-- len is optional
sb.append(propositions[0]);
for (int i = 1; i < propositions.length; i++) {
    sb.insert(0, ",").insert(0, propositions[i]).insert(0, "|(").append(")");
}
System.out.println(sb);

【讨论】:

  • 我要试试这个方法,谢谢大家的回复。
猜你喜欢
  • 1970-01-01
  • 2011-05-15
  • 2014-02-02
  • 2020-11-30
  • 2021-05-12
  • 1970-01-01
  • 2016-02-08
  • 2016-02-22
  • 1970-01-01
相关资源
最近更新 更多