java 中没有分配的字符串连接

String concatenation without allocation in java

有没有办法在不分配内存的情况下连接两个字符串(不是最终的)?

例如,我有这两个字符串:

final String SCORE_TEXT = "SCORE: ";
String score = "1000"; //or int score = 1000;

当我连接这两个字符串时,会创建一个新的 String 对象。

font.drawMultiLine(batch, SCORE_TEXT + score, 50f, 670f);//this creates new string each time

由于这是在主游戏循环中完成的(一秒内执行~60次),所以有很多分配。

我可以在没有分配的情况下以某种方式做到这一点吗?

第一次没听懂这个问题。您是否尝试过使用以下方法?

SCORE_TEXT.concat(score);

我认为您不能在不为其分配内存的情况下填充一个值。您能做的最好的事情是创建一个全局字符串变量并为其提供 SCORE_TEXT + 分数的值。在 font.drawMultiLine() 方法中使用该全局字符串变量。

通过这种方式,您可以最大限度地减少分配的内存量,因为内存只分配一次,并且同一位置会一次又一次地更新。

Seems that drawMultiLine 不接受 String,而是 CharSequence。因此,您可能会实现自己的 CharSequence,它实际上并不连接两个字符串。这是实施草案:

public class ConcatenatedString implements CharSequence {
    final String left, right;
    final int leftLength;

    public ConcatenatedString(String left, String right) {
        this.left = left;
        this.right = right;
        this.leftLength = left.length();
    }

    @Override
    public int length() {
        return leftLength+right.length();
    }

    @Override
    public char charAt(int index) {
        return index < leftLength ? left.charAt(index) : right.charAt(index-leftLength);
    }

    @Override
    public CharSequence subSequence(int start, int end) {
        if(end <= leftLength)
            return left.substring(start, end);
        if(start >= leftLength)
            return right.substring(start-leftLength, end-leftLength);
        return toString().substring(start, end);
    }

    @Override
    public String toString() {
        return left.concat(right);
    }
}

这样使用:

font.drawMultiLine(batch, new ConcatenatedString(SCORE_TEXT, score), 50f, 670f);

在你的情况下 drawMultiline 内部只需要 lengthcharAt 方法。使用 ConcatenatedString 您只创建一个新对象。相反,当您使用 SCORE_TEXT + score 时,您创建了一个临时 StringBuilder,它在内部创建 char[] 数组,复制输入符号,在必要时调整数组大小,然后创建最终的 String创建新 char[] 数组并再次复制符号的对象。因此 ConcatenatedString 可能会更快。

显而易见的解决方案是不在每一帧上重新创建输出 String,而仅在它发生变化时才重新创建。

实现此目的的一种方法是将其存储在主循环之外的某个位置,并在特定事件发生时更新它,即 "score" 实际发生变化。在您的主循环中,您只需使用预先创建的 String

如果您t/or不想使用这种基于事件的方法,您可以始终存储 "previous" 分数,并且仅在前一个分数与当前分数不同时才连接一个新字符串当前分数。

根据您的分数实际变化的频率,这应该会减少大部分重新分配。当然,除非分数以 60 fps 的速度变化,在这种情况下,这整点是完全无声的,因为没有人能够阅读您正在打印的文本。

String 在 Java 中被设计为不可变的。使用 StringBuilder