Go的基本数据类型string和slice | 青训营笔记

74 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天, 今天主要学习总结了Go的两种基本类型,string和slice

string

数据结构

type StringHeader struct {
    Data uintptr
    Len  int
}

字符串在Go语言内存模型中用一个2字长的数据结构表示。它包含一个指向字符串存储数据的指针和一个长度数据。因为string类型是不可变的,对于多字符串共享同一个存储数据是安全的。切分操作str[i:j]会得到一个新的2字长结构,一个可能不同的但仍指向同一个字节序列(即上文说的存储数据)的指针和长度数据。这意味着字符串切分可以在不涉及内存分配或复制操作。这使得字符串切分的效率等同于传递下标。

(说句题外话,在Java和其他语言里有一个有名的“疑难杂症” :在你分割字符串并保存时,对于源字符串的引用在内存中仍然保存着完整的原始字符串--即使只有一小部分仍被需要,Go也有这个“毛病”。另一方面,我们努力但又失败了的是,让字符串分割操作变得昂贵--包含一次分配和一次复制。在大多数程序中都避免了这么做。)

slice

数据结构

一个slice是一个数组某个部分的引用。在内存中,它是一个包含3个域的结构体:指向slice中第一个元素的指针,slice的长度,以及slice的容量。长度是下标操作的上界,如x[i]中i必须小于长度。容量是分割操作的上界,如x[i:j]中j不能大于容量。

struct    Slice
{    // must not move anything
    byte*    array;        // actual data
    uintgo    len;        // number of elements
    uintgo    cap;        // allocated number of elements
};

扩容

在对slice进行append等操作时,可能会造成slice的自动扩容。其扩容时的大小增长规则是:

  • 如果新的大小是当前大小2倍以上,则大小增长为新大小
  • 否则循环以下操作:如果当前大小小于1024,按每次2倍增长,否则每次按当前大小1/4增长。直到增长的大小超过或等于新大小。

时间复杂度为

4N44N-4

N=1.25MN=1.25^M

T=1+1.25+1.252+.....+1.25mT={1+1.25+1.25^2+.....+1.25^m}

T=1(11.25m)11.25T = \frac {1*(1-1.25^m)}{1-1.25}

T=1(1.25m1)0.25T = \frac {1*(1.25^m-1)}{0.25}

T=4(1.25m1)T = 4*(1.25^m-1)

T=4(N1)T = 4*(N-1)