This is a creation in Article, where the information may have evolved or changed.
The content of this article is my memo to go language variables, constants, arrays, slices, mappings, structures, and records the key relevant knowledge points for the search.
If there are errors in the text, please point out, so as not to mislead! Refer to this document: Go Language Memo (1): Basic data structure, Thank you!
Reference book "The Go Programming Language", "Go in Action","Go language study notes", etc.
Directory:
- Variable
- Constant
- Array
- Slice
- Mapping
- Structural body
One, variable
- A variable is one or more pieces of memory used to store data;
- Variables always have a fixed data type, and the type determines the length of the occupied memory and the storage format;
- The compiled code uses the memory address of the variable to access the data, not the variable name;
- A short variable declaration can only be declared within a function (local variable), and Var declaration is unrestricted (but is generally used to declare variables that are not explicitly initialized);
- When a variable of the same name is declared in the same scope, it is rolled back to assignment, that is, the variable must be reused (at least one new variable definition);
- The variable with the same name declared in different scopes is redefined (overwritten);
var q intvar y = 453var ( n,m = 134, "SRF" N1,m1 int) func F1 () { n,m: =, "sss" n,m1: =, "yyy" fmt. Println (N,M,M1) n = n+5//assignment expression, first evaluates the rvalue //"_" Null identifier used to temporarily circumvent the compiler's error checking for unused variables and importing packages if _,ok: = ADD1 (n); OK { FMT . PRINTLN (n) }}func add1 (n int) (int, bool) { return n+1,true}
Two, constants, enumerations
- A constant is an immutable value, which can be a literal, or an expression that the compiler can calculate the result of. Unused constants do not cause compilation errors;
- If the type and initial value are not specified in a constant group, the value of the previous row is the same as the non-empty constant right;
- Constants are directly expanded by the compiler in the preprocessing phase, and are used as instruction data, so the constant address cannot be taken.
Const I = 5const ( x byte = 1 x1 x2 //x1,x2 are 1 s = "abs" S1 //s1= "abc") const ( _,_ int = i Ota,iota*3//0,0*3 ignores the value and explicitly specifies the type int k1,k2 //1,1*3 l1,l2 //2,2*3 o1,o2 = 5,6 //Interrupt iota self-increment r1,r2 //5,6 the same line e1,e2 = iota,iota*3//5,5*3 restore iota, increment by row)//enum type color Byteconst ( blue color = Iota Red Green) Func main () { t:= blue fmt. Println (t)//0 //fmt. PRINTLN (&i)///Error: Unable to take address to constant cannot takes the addresses of i}
Three, array
- Arrays are the underlying data structures for slices and mappings. An array is a value type, and the entire array is copied when the array is assigned and passed.
- An array is a fixed-length data type that stores a contiguous block of memory with elements of the same data type.
- Because the memory used by the array is continuously allocated, the query, modification, and so on of the elements are very fast.
- How to declare an array:
- var array1 [5]int
- Array1: = [5]int{3,5,6,3,2}
- Array1: = [...] int{3,4,7,8,1}//determines the length of an array based on the number of elements in the array literal
- Array1: = [5]int{0:3,3:5,4:8}//Initialize only the elements of the specified index, remaining elements remain 0 values
- Array1: = [...] INT{1,2,9:32}
- The type of an array element can be any built-in type, or it can be a struct type, or a pointer type.
- The type of the array variable includes the array length and the element type, and only two parts of the same array can be assigned to each other.
- Multidimensional arrays: The array itself has only one dimension, which can only be created by combining multiple arrays; The built-in function Len and cap all return the length of the first dimension
- var array [4][2]int
- Array: = [4][2]int{2:{20,21},3:{41,25}}
- Array: = [4][2]int{2:{1:21},3:{0:41}}
- Array: = [...] [4]int{{2,3},{4,5}}//Only the first dimension allows the use of "..."
- ARRAY[2][1] = 10
- Passing an array between functions: Because the passed variable is always a copy of the value of the variable, because the array is a value type, the entire array is copied when the array variable is assigned and passed! When defining a function, the parameter should be designed as a pointer type for a large data type , so that when the function is called, only 8 bytes of memory is allocated to each pointer on the stack, but this means that the value that the pointer points to (shared memory) is changed, but in most cases the slice type should be used instead of the array.
- Note: Because the underlying array of slices may allocate memory on the heap, the consumption of the copy on the stack for the fractional group may be less than the make cost;
four, sliced slice
- A slice slice is a reference type that internally references an underlying array through a pointer and sets the correlation property to limit the read and write operations of the data to the specified area.
The slice itself is a read-only object that works like an array pointer to a wrapper type slice struct{ array unsafe. Pointer len Int//read-write number of elements cap INT//True length of referenced array fragment}
- creation and initialization:
- Slice1: = Make ([]string, 5)//Create a slice of type string of length, capacity 5
- Slice1: = made ([]s Tring, 3, 5)//Create a slice of type string with a length of 3 and a capacity of 5
- slice2: = []string{"Red", "Blue", "green"}//length and size 3 slices
- s Lice2: = []int{99:1}//length and capacity are 100, and initialization 100th element is 1
- Slice again Reslice: Determines the referenced array fragment at the beginning and end of the index position of the original slice, and does not support reverse indexing. The actual range is a right half open interval
Assuming that the original slice slice capacity is k, the new slice newslice as the index I element position of the original slice, and the value within the capacity range of the original slice
- newslice: = slice[I:j] The length is j-i, the capacity is K-i
- newslice: = slice[i:j: n]//limits the capacity of the new slice to N-i (the third parameter n-1 represents the element index of the last visible underlying array portion that the new slice can extend to, thus reaching the target of limiting capacity , note: n must be >=j)
- The new slice cannot access the part before the first element of the underlying array it points to (the part before the first index)
- example: ss:=[]int{10,20,30,40,50} Newss:=ss[2:4:5] //NEWSS for [30,40] with a capacity of 3
- new and old slices pointing to the same underlying array;
Use Reslice to implement a stack structure (you can also define a stack as a type) var stack = make ([]int,0,5) func push (x int) error {N:=len (stack) if n = = Cap (stack) {R Eturn errors. New ("Stack is full")}stack = stack[:n+1]//Add an accessible element Stack[n]stack[n]=xreturn Nil}func pop () (int, error) {N:=len (stack) if n = = 0 {return 0,errors. New ("Stack is empty")}x:=stack[n-1]stack = stack[:n-1]///The newer stack reduces an accessible element Stack[n-1]return X,nil}func main () {for i: = 0; I < 7; i++ {fmt. Printf ("Push%d:%v,%v\n", I,push (i), stack)}for I: = 0; I < 7; i++ {x,err:=pop () fmt. Printf ("Push%d:%v,%v\n", X,err,stack)}}
- The length of the slice can automatically grow or shrink as needed:
- Dynamic growth is achieved through the append () function.
- Shrinking is done by slicing it again, and new slices obtained from the slice will share the underlying array with the original slice, and their pointers point to the same underlying array.
- Since the slice simply refers to the underlying array, the data for the underlying array is not part of the slice itself, so a slice requires only 24 bytes of memory (on a 64-bit machine): Pointer field 8 bytes, Length field 8 bytes, capacity field 8 bytes. So it is efficient to pass slices directly between functions, allocating only 24 bytes of stack memory.
- Nil slices and empty slices:
- Nil Slice: A slice that is only declared but not initialized, such as Var Slice1 []int,nil Slice can be used to describe a nonexistent slice
- Empty slices: slices with both length and capacity of 0, creating an empty slice without allocating any memory to the underlying array element, which can be used to represent an empty collection, such as Slice1: = Make ([]int, 0), Slice2: = []int{}
- Call built-in functions like Append, Len, and cap for nil slices and empty slices
- Append () function:
Slice = append (slice, elem1, elem2)//can append multiple values at one time
Slice = append (slice, anotherslice ...) Use "..." to append all elements of anotherslice to slice
- When slice has available capacity, the append () operation merges the available elements into the length of the slice, assigns it a value, and finally returns a completely new slice (sharing the same underlying array with the old slice);
- If the capacity of the slice is insufficient, the append () operation creates a new underlying array, copies the referenced old values into the new array, and appends the new values;
- When the original slice capacity is insufficient, and less than 1000, then the capacity of the new slice is twice times the original capacity, if greater than 1000, the growth factor of capacity becomes 1.25;
- Due to insufficient capacity, the append operation returns a new slice with its own independent underlying array, which does not share the same underlying array as the original slice, and the modification of the new slice element will not affect the underlying array of the original slice, tip: Set the length equal to the capacity when creating the slice. This forces the creation of a new underlying array to be separated from the original array at the first append operation, such as newslice: = Oldslice[2:3:3]
- copy function: Copies data between two slice objects, allowing you to point to the same underlying array, allowing the target interval to overlap. The final copy length is based on the shorter slice length
- The iterations of the slice are as follows: For index, Value: = Range slice{....},index is the current iteration to the index location, value is the copy from slice, the memory address of index and value variable is constant, but the point of the value is constantly updated.
- The Len function returns the length of the slice, and the CAP function returns the slice's capacity
- Multidimensional slices (like jagged arrays): slices and arrays are themselves one-dimensional, and multiple slices can be combined to form a multidimensional slice, such as: slice: = [][]int{{12},{34,23}},slice[0] {12},slice[1] = {34,23}
- Note: If the slice references a small fragment in a large array for a long time, you should copy the required data and create a new independent slice so that the original large array memory can be reclaimed in time;
Five, mapping map
- Mapping map: Is an unordered collection of key-value pairs that can quickly retrieve data based on a key, which, like an index, points to the value associated with the key;
- The mapping is unordered, and the order may be different each time it iterates, because the map uses a hash table internally;
- The mapped hash table contains a set of buckets, each of which stores a subset of key-value pairs;
- The mapping internally uses two arrays:
- First array: A high eight-bit value that stores the hash key for the selection bucket, which is used to distinguish which bucket each key-value pair is to exist in;
- Second array: Each bucket has a byte array, which stores all the keys in the bucket, and then stores all the values of the bucket;
- When storing, deleting, and locating a mapped key-value pair, the specified key is passed to the mapped hash function, which converts the key to a hash value, and then chooses which bucket to match the hash value to the first array, and then finds the corresponding key and value in the byte array in the bucket;
- To create and initialize a map:
- Dict1: = Make (map[string]int)//null mapping, equivalent to Dict1: = map[string]int{}
Dict1: = Make (Map[string]int, 1000)//pre-allocate enough memory space to help improve performance
Dict2: = map[string]int{"SRF": 143, "ROBT": 342}
- Mapped keys: can only be types that can be compared with "= =", but not slices, functions, and types that contain slices because they have referential semantics. The value of the map can be any type;
- The nil mapping is a declaration-only, uninitialized mapping that cannot be used directly, such as Var dict map[string]int. The empty mapping can be used directly;
- The 0 value of the map type is nil, which means that no hash table is referenced. Before you can save data to a map, you must first create a map that refers to a hash table.
- If you want to store a large number of small objects with map, you should store them directly as value types, not pointer types, to help reduce the number of objects that need to be scanned, and significantly shorten the garbage collection time;
- To take a value from the map:
- Value: = dict2["SRF"]//key is present when the corresponding value is returned, no 0 value of the return type exists
- Value, OK: = dict2["SRF"]//ok the Boolean flag for whether the key exists
If OK {...}
- The element in map is not a variable, and we cannot take the element of the map (because map may reallocate memory when it grows dynamically), so you cannot directly modify the value member, but you should modify it by temporary variable or define the value as a pointer type:
M: = users[int]user{ 1:{"SRF", 25}}//m[1].age +=1//error, unable to set value u: = m[1]u.age+=1m[1] = u
- Traversal mappings:
- For key: = Range Dict2 {...}//Receive key only
- For key, Value: = Range dict2 {...}//Receive both the key and the value
- When you traverse a map, you can add, remove members
- The order of the key-value pairs that traverse the mappings is random, to get the mapped key-value pairs in order, you need to walk out the mapped key to a slice, then sort the slice, and finally traverse the slice to map the corresponding values in the order of the elements in the slice
- Delete (Dict2, "SRF") removes the specified key-value pair from the map;
- The runtime detects the mapped concurrency operation and only synchronizes the mapped operation (only one task can be in action mapping at the same time), or it can cause the process to crash. Read-write lock sync available. Rwmutex enables synchronization to avoid simultaneous read and write operations:
Func main () {var lock sync. Rwmutexm:=make (Map[string]int) go func () {for {lock. Lock () m["a"] + = 1lock. Unlock () //cannot be used with defertime. Sleep (time. microsecond)}} () go func () {for {lock. Rlock () _ = m["B"]lock. Runlock () time. Sleep (time. microsecond)}} () Select {}//block process exit}
- Passing a map between functions is the same as passing a slice (without having to take the address again), passing only a copy of the mapping itself, without copying all the underlying data structures referenced by the map, and the modifications to that mapped copy will be reflected in all references to that mapping.
- Multidimensional mapping: A mapping with a value of a mapping type. It should be noted that mapping as a value also needs to be initialized before it can be used, such as:
var m1 = make (map[int]map[string]string)
m1[13]= map[string]string{"SRF": "Yes"}
- A function that determines whether two maps are equal:
Func equal (x, y Map[string]int) bool { If Len (x)! = Len (y) { return false } for K, XV: = range x { if YV, OK: = Y[k];!ok | | YV! = XV { return False } } return True}
- A collection of strings represented by a map set:
M:=make (Map[string]bool) if!m["SRF"] { m["SRF"] = true}
Vi. Structural Bodies
- struct struct is a compound type, which is packaged by several different types of named Field series.
- Field names must be unique, with "_" complement, supporting the use of their own pointer-type members (this allows us to create recursive data structures, such as linked lists and tree structure, etc. );
Type node struct{ _ int ID int ' account ' next *node}
- Structure type information includes: Field name, field label, order of arrangement, only three are all the same to be considered the same type;
- All fields can be initialized sequentially, but it is recommended that you initialize some or all of the fields in a named way (you can ignore the order in which the fields are defined, making it easier to modify and expand the order of the members of the structure);
- Structure comparison: Only if all members of the struct are comparable, then the struct is comparable
- An anonymous struct type can be defined directly, assigned to a variable, or used as the type of the field (the anonymous struct field cannot be initialized directly in literal form and requires ".") Syntax to initialize its members)
U: = struct{ name string}type file struct{ name string attr struct{ owner int Perm int }}f: = f Ile{name: "Test.dat"}f.attr.owner = 1f.attr.perm = 0755
- Empty structure: struct{}, a length of 0, does not allocate memory, and all other objects with a length of 0 usually point to the runtime.zerobase variable (i.e. they all point to the same variable); The empty structure type is often used as the type of the channel element for event notification (the advantage is that it does not account for memory);
- Anonymous field (embedded type): That is, no explicit name is specified, only the type of field:
- The compiler implicitly takes the name of the type as a field (without the package name);
- The outer structure not only obtains all the members of the anonymous member type, but also obtains the whole method of exporting the type;
- The members of the embedded Type field can be referenced directly, but the entire structure of it must be explicitly initialized when initialized with the literal syntax;
- The data type of the member of the anonymous field must be a named type or a pointer to a named type, not an interface pointer and a multilevel pointer;
- The underlying type and its pointer type cannot be used as anonymous fields at the same time
- Field name Processing: priority is given to the outer field (the inner field is obscured, accessible only through fully qualified names), and for multiple identically named fields with the same same hierarchy must be accessed by a fully qualified name, otherwise the compiler cannot determine the target;
- Field Label: The metadata used to describe a field, which is not a data member but is part of the type information, which can be used to obtain the label information of a field at run time, which is often utilized as a format test (such as JSON), a database relational mapping, and so on; standard library reflect. Structtag provides the ability to divide/parse tags;
Type user struct{ name string ' nickname ' Sex byte ' sex '}func main () { u:=user{"TOM", 1} v:=reflect. ValueOf (U) t:=v.type () for I,n:=0,t.numfield (); i<n;i++{ FMT. Printf ("%s:%v\n", T.field (i). Tag, V.field (i)) }}
- Regardless of the number of fields in the struct, its memory is always allocated one at a time, and the fields are arranged in a defined order (including all members of the embedded field) in the adjacent address space. For reference types, strings, pointers, the structure memory contains only its basic (header) data.
- When a struct allocates memory, memory alignment is performed (according to the longest underlying type width in all fields), except that the compiler treats the empty struct type field as one of the last fields as a single alignment (avoiding cross-border).
- Memory alignment is related to the hardware platform and access efficiency (the CPU needs less read cycles to access naturally aligned data and avoids splicing data)