在Go中创建2D切片的简洁方法是什么?

时间:2016-10-01 09:11:01

标签: go slice

我正在通过A Tour of Go学习Go。其中一个练习要求我创建包含dy的{​​{1}}行和dx列的2D切片。我目前采用的方法是:

uint8

我认为迭代每个切片来初始化它太冗长了。如果切片具有更多尺寸,则代码将变得难以处理。是否有一种简洁的方法来初始化Go中的2D(或n维)切片?

4 个答案:

答案 0 :(得分:83)

没有更简洁的方式,你所做的就是“正确”的方式;因为切片总是一维的,但可以组成以构造更高维的对象。有关详细信息,请参阅此问题:Go: How is two dimensional array's memory representation

您可以简化的一件事是使用for range构造:

a := make([][]uint8, dy)
for i := range a {
    a[i] = make([]uint8, dx)
}

另请注意,如果使用composite literal初始化切片,则可以“免费”获取此切片,例如:

a := [][]uint8{
    {0, 1, 2, 3},
    {4, 5, 6, 7},
}
fmt.Println(a) // Output is [[0 1 2 3] [4 5 6 7]]

是的,这有其局限性,因为看似你必须列举所有元素;但是有一些技巧,即你不必枚举所有值,只需要那些不是切片元素类型的zero values的值。有关详细信息,请参阅Keyed items in golang array initialization

例如,如果您想要前10个元素为零的切片,然后是12,则可以像这样创建:

b := []uint{10: 1, 2}
fmt.Println(b) // Prints [0 0 0 0 0 0 0 0 0 0 1 2]

另请注意,如果您使用arrays代替slices,则可以非常轻松地创建它:

c := [5][5]uint8{}
fmt.Println(c)

输出是:

[[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]]

对于数组,您不必遍历“外部”数组并初始化“内部”数组,因为数组不是描述符而是值。有关详细信息,请参阅博文Arrays, slices (and strings): The mechanics of 'append'

尝试Go Playground上的示例。

答案 1 :(得分:5)

有两种使用切片创建矩阵的方法。让我们看看它们之间的区别。

第一种方法:

matrix := make([][]int, n)
for i := 0; i < n; i++ {
    matrix[i] = make([]int, m)
}

第二种方法:

matrix := make([][]int, n)
rows := make([]int, n*m)
for i := 0; i < n; i++ {
    matrix[i] = rows[i*m : (i+1)*m]
}

关于第一种方法,连续进行make调用并不能确保最终得到连续的矩阵,因此可能会将矩阵划分为内存。让我们考虑一个带有两个Go例程的示例,该例程可能导致这种情况:

  1. 例程#0运行make([][]int, n)来为matrix获取分配的内存,从而从0x000到0x07F获得一块内存。
  2. 然后,它开始循环并执行第一行make([]int, m),从0x080到0x0FF。
  3. 在第二次迭代中,它被调度程序抢占。
  4. 调度程序使处理器进入例程1,然后开始运行。此程序也使用make(出于自己的目的),并从0x100到0x17F(在例程#0的第一行旁边)。
  5. 过一会儿,它将被抢占,例程#0重新开始运行。
  6. 它执行与第二次循环迭代相对应的make([]int, m),并为第二行从0x180变为0x1FF。至此,我们已经得到了两行。

使用第二种方法,例程执行make([]int, n*m)来获取在单个片中分配的所有矩阵,从而确保连续性。之后,需要循环以将矩阵指针更新为与每一行相对应的子切片。

您可以使用Go Playground中上面显示的代码来查看使用这两种方法分配的内存的差异。请注意,我使用runtime.Gosched()只是为了产生处理器并强制调度程序切换到另一个例程。

使用哪个?想象第一种方法最坏的情况,即每一行在内存中都不是另一行。然后,如果您的程序遍历矩阵元素(以读取或写入它们),则由于数据位置更差,与第二种方法相比,可能会出现更多的高速缓存未命中(因此导致更高的延迟)。另一方面,使用第二种方法,尽管理论上可能有足够的可用内存,但由于内存碎片(块散布在整个内存中),可能无法为矩阵分配单个内存

因此,除非存在大量的内存碎片,并且要分配的矩阵足够大,否则您总是想使用第二种方法来利用数据局部性。

答案 2 :(得分:1)

这里有一个很简单的方法:

value := [][]string{}{[]string{}{"A1","A2"}, []string{}{"B1", "B2"}}

PS.:您可以将“字符串”更改为您在切片中使用的元素类型。

答案 3 :(得分:0)

您可以参考这段代码 -

Call<List<MyTransaction>> listCall = apiService.getTransaction("420");
    listCall.enqueue(new Callback<List<MyTransaction>>() {
        @Override
        public void onResponse(Call<List<MyTransaction>> call, Response<List<MyTransaction>> response) {

            List<MyTransaction> examples = response.body();

            Log.e(TAG, "Response : " + response.body());// Response : null
            for (int i = 0; i < examples.size(); i++) {
                Log.e(TAG, "examples list : " + examples.get(i).categoryType);
            }
        }

        @Override
        public void onFailure(Call<List<Example>> call, Throwable t) {

        }
    });

Reference