在Swift中追加字符以形成字符串的最快,最简洁的方法

Dai*_*Dai 24 string swift

我来自C#背景,其中System.String是不可变的,字符串连接相对昂贵(因为它需要重新分配字符串)我们知道使用该StringBuilder类型,因为它预先分配一个更大的缓冲区,其中单个字符(Char,16位值类型)和短字符串可以廉价连接而无需额外分配.

我正在将一些C#代码移植到Swift,它从[Bool]字节长度小于8位的子八位字节索引读取位数()(这是一种非常节省空间的文件格式).

我的C#代码做了这样的事情:

 StringBuilder sb = new StringBuilder( expectedCharacterCount );
 int idxInBits = 0;
 Boolean[] bits = ...;
 for(int i = 0; i < someLength; i++) {
     Char c = ReadNextCharacter( ref idxInBits, 6 ); // each character is 6 bits in this example
     sb.Append( c );
 }
Run Code Online (Sandbox Code Playgroud)

在Swift中,我假设NSMutableString相当于.NET StringBuilder,我发现这个QA关于附加单个字符(如何在Swift中将字符附加到字符串?)所以在Swift中我有这个:

var buffer: NSMutableString
for i in 0..<charCount {
    let charValue: Character = readNextCharacter( ... )
    buffer.AppendWithFormat("%c", charValue)
}
return String(buffer)
Run Code Online (Sandbox Code Playgroud)

但我不知道为什么它首先通过格式字符串,这看起来效率低(在每次迭代时重新格式化格式字符串)并且因为我的代码在iOS设备上运行,我希望对我的程序的CPU和内存非常保守用法.

在我写这篇文章的时候,我学会了我的代码应该真正使用UnicodeScalar而不是Character,问题是NSMutableString不会让你追加一个UnicodeScalar值,你必须使用Swift自己的可变String类型,所以现在我的代码看起来像:

var buffer: String
for i in 0..<charCount {
    let x: UnicodeScalar = readNextCharacter( ... )
    buffer.append(x)
}
return buffer
Run Code Online (Sandbox Code Playgroud)

我认为这String是不可改变的,但我注意到它的append方法返回Void.

我仍然感到不舒服,因为我不知道Swift的String类型是如何在内部实现的,我不知道如何预分配大缓冲区以避免重新分配(假设Swift String使用不断增长的算法).

dfr*_*fri 26

(这个答案是根据对Swift 2和3有效的文档和源代码编写的:一旦Swift 4到达,可能需要更新和修改)

由于Swift现在是开源的,我们实际上可以查看Swift源代码:s native String

从上面的来源,我们有以下评论

/// Growth and Capacity
/// ===================
///
/// When a string's contiguous storage fills up, new storage must be
/// allocated and characters must be moved to the new storage.
/// `String` uses an exponential growth strategy that makes `append` a
/// constant time operation *when amortized over many invocations*.
Run Code Online (Sandbox Code Playgroud)

鉴于上述情况,你不应该需要担心追加字符的斯威夫特(无论是通过性能append(_: Character),append(_: UniodeScalar)appendContentsOf(_: String)),为连续存储的重新分配一定String情况下不应该的是需要单个字符非常频繁WRT号为此重新分配附加.

另请注意,NSMutableString它不是"纯粹本机" Swift,而是属于桥接Obj-C类的族(可通过访问Foundation).


您的评论的注释

"我认为这String是不可改变的,但我注意到它的追加方法会返回Void."

String 只是一个(值)类型,可以由可变属性和不可变属性使用

var foo = "foo" // mutable 
let bar = "bar" // immutable
    /* (both the above inferred to be of type 'String') */
Run Code Online (Sandbox Code Playgroud)

变异空隙返回实例方法append(_: Character)append(_: UniodeScalar)可访问到可变以及不可变的String情况下,但自然地使用它们与后者将产生编译时错误

let chars : [Character]  = ["b","a","r"]
foo.append(chars[0]) // "foob"
bar.append(chars[0]) // error: cannot use mutating member on immutable value ...
Run Code Online (Sandbox Code Playgroud)