Golang 이해 - 그리고 조각의 배열

배열

배열의 정의와 이동의 특성

배열은 고정 길이로 이루어지는 특정 타입의 요소들의 시퀀스, 그것은 0 또는 그 이상의 요소들의 어레이로 구성 될 수있다.

때문에 数组的长度是固定的때문에 거의 배열로 이동 언어에 직접 사용하지 않습니다.

종류 및 배열 슬라이스 (슬라이스), 그것은 성장하고 동적 순서, 더 유연 슬라이스 기능을 축소 할 수 있습니다 대응, 그것은 그 조각 작품을 이해, 우리는 배열을 이해할 필요가있다.

기본적으로, 상기 어레이의 각 요소 값이 제로가 요소의 유형에 대응하여 초기화되고, 그 숫자 타입 0이다. 또한 어레이를 초기화하는 값의 세트를 사용하여 배열 리터럴 구문을 사용할 수있다 :

var q [3]int = [3]int{1, 2, 3}
var r [3]int = [3]int{1, 2}
fmt.Println(r[2]) // "0"

어레이의 종 방향 위치에 존재하는 경우 배열 리터럴 "..."생략, 그것은 초기 값의 수에 기초하여 계산된다 어레이의 길이를 나타내는 것이다. 따라서, 상기 어레이 (Q)의 정의를 간소화 할 수있다

q := [...]int{1, 2, 3}
fmt.Printf("%T\n", q) // "[3]int"

数组的长度是数组类型的一个组成部分따라서, [3] 및 INT [4] INT는 어레이의 두 가지 유형이다. 어레이의 길이가 컴파일 시간에 결정되기 때문에, 어레이의 길이는 일정한 표현해야한다.

우리는 배열, 조각,지도를 발견하고 구조를 그대로 표현은 매우 유사하다. 다음으로 상기 초기화 시퀀스는, 직접 값들의 시퀀스를 형성하고, 또한 값과 대응하는 인덱스 모드 초기화 목록을 지정할 수있다 :

type Currency int

const (
    USD Currency = iota // 美元
    EUR                 // 欧元
    GBP                 // 英镑
    RMB                 // 人民币
)

symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}

fmt.Println(RMB, symbol[RMB]) // "3 ¥"

如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的이번에는 우리가 할 수 直接通过==比较运算符두 배열을 비교 只有当两个数组的所有元素都是相等的时候数组才是相等的. 동일하지 않은 비교 연산자! = 동일한 규칙을 따릅니다.

어떻게 배열 함수 파라미터를 전달할

각 통화의 기능 매개 변수는 내부 함수 매개 변수 변수에 할당 될 때 함수를 호출 할 때 函数参数变量接收的是一个复制的副本원래 호출하지 변수.

이 때문에 函数参数传递的机制导致传递大的数组类型将是低效的, 어레이 파라미터를 변경 복사 어레이에서 발생된다 并不能直接修改调用时原始的数组变量.

이와 관련, 이동 언어와 다른 많은 프로그래밍 언어의 배열을 치료하는 방법으로, 다른 프로그래밍 언어는 암시 적으로 함수에 전달 된 객체에 대한 참조 또는 포인터로 배열이라고 할 수있다.

주의 사항

파라미터 포인터 배열 고도로 효율적인 전달 또한 함수 내의 배열의 값을 수정할 수있게하면서,

어레이의 유형은 경질의 길이 정보를 포함하고 있기 때문에, 어레이는 경질 타입은 여전히있다.

그리고 배열은 배열 요소를 추가하거나 삭제할 수 없습니다. 특정 예외가 배열의 특정 크기를 해결해야 이러한 이유로, 배열은 여전히 ​​거의 함수 매개 변수로 사용하지 않는 대신, 우리는 일반적으로 배열 대신 슬라이스를 사용합니다.


일부분

슬라이스 정의와 이동의 특성

슬라이스 (슬라이스) 각각 서열 요소는 동일한 형태를 가지며, 가변 길이의 서열을 나타낸다. 보통 T 슬라이스 요소의 형식을 나타내는 슬라이스 형 [] T, 쓰기와 어레이 슬라이스 구 등을하지만, 고정 길이 아니다.

데이터 조각 경량 구조, 기능 요소들의 어레이 서열 (또는 모두)에 대한 액세스를 제공하고, 바닥 슬라이스는 어레이 오브젝트에 대한 참조를한다.

포인터, 길이, 용량 : 한 조각은 세 부분으로 구성되어 있습니다.

  • 슬라이스의 어드레스에 대응하는 기본 배열 요소의 첫 번째 요소의 포인터, 제 슬라이스의 첫 번째 요소는 반드시 배열의 요소 아니라는 것을 주목해야
  • 슬라이스의 길이에 대응하는 요소의 수
  • 길이가 용량을 초과 할 수있는 능력은 기본 데이터의 슬라이스의 선두 위치에 최종 위치에서 일반적이다. 내장 함수는 렌과 캡 길이와 볼륨 조각을 반환합니다.

내장 함수는 렌과 캡 길이와 볼륨 조각을 반환합니다.

기본 데이터는 복수의 슬라이스들 사이에서 공유 될 수 있고, 상기 기준 영역은 상기 어레이의 부분 중첩 될 수있다.

IMG

슬라이싱 동작 상한 캡 (들)을 초과하는 경우 공황 예외가 발생하지만, 새로운 슬라이스의 길이가 커지기 때문에 LEN (S) 너머로 확장, 슬라이스를 의미

때문에 slice值包含指向第一个slice元素的指针따라서 向函数传递slice将允许在函数内部修改底层数组的元素.

즉,复制一个slice只是对底层的数组创建了一个新的slice别名

비교하지 (과 다를 배열), 슬라이스 사이에서는, 우리는 모든 동등한 요소 개의 슬라이스가 포함되어 있는지 여부를 결정하기 위해 "=="연산자를 사용할 수 없다.

그러나, 표준 라이브러리가 높은 두 바이트 슬라이스 ([] 바이트)와 동일하지만, 슬라이스의 다른 유형, 우리는 각각의 요소에 자신을 비교하기 시작해야하는지 여부를 결정하는 기능을 제공 bytes.Equal 최적화 :

func equal(x, y []string) bool {
    if len(x) != len(y) {
        return false
    }
    for i := range x {
        // 逐个元素比较
        if x[i] != y[i] {
            return false
        }
    }
    return true
}

왜 슬라이스 비교를 지원하지 않습니다?

  1. 요소의 조각 간접적으로 슬라이스도 자신을 포함 할 수 있습니다, 참조됩니다. 가 있지만이 상황을 처리하는 방법에는 여러 가지가 있지만, 아무도 간단하고 효과적인 없다.
  2. 슬라이스의 요소가 간접 참조 고정 슬라이스 값이므로 : 배열을 기본 요소가 들어가 변형 될 수 있기 때문에, 다른 시간에 다른 구성 요소를 포함 할 수있다 (주석 값 슬라이스 자체가 아닌 요소를 말한다). 불변 필요로하는지도에만 간단한 얕은 복사 키, 키 홀더 : 라이프 사이클 전반에 걸쳐 (주석 예를 들어, 슬라이스 확장을, 자신의 / 주소의 값이 변화의 원인이됩니다).
  3. 참고로, 포인터 타입 등 찬 == 항등 테스트 같이 2 개의 참조 여부 같은 객체에 기초

우리는 위의 이유는 安全的做法是直接禁止slice之间的比较操作, 유일한 합법적 인 비교 연산을 슬라이스 상대적으로 전무하다.

슬라이스를 비교 전무하지만, 일부의 될 수 있지만 이는 또한 세부 사항에주의를 지불해야합니다 :

  1. 슬라이스 닐 제로의 값과 동일하다. 슬라이스 닐 값이 아닌 기본 배열.
  2. 슬라이스의 길이와 용량의 무 값은 전부 0이지만,이 예를 들어, 길이 및 비 닐 값 용량 슬라이스 0 [] INT {} 또는 확인 ([] 지능, 3) [3]
  3. 당신이 조각이 비어 있는지 여부를 테스트해야하는 경우의 == 전무로 판단해서는 안 결정하기 위해 렌 (들) == 0을 사용합니다. 외측과 동일한 무 비교에 더하여, 동작 및 0과 동일한 길이의 다른 슬라이스의 슬라이스 닐 값;

슬라이스 메이크업 요소 유형을 만들 수있는 기능을 내장하고, 용량은 길이를 지정했습니다. 용량 부가되는 경우에는, 용량은 길이와 동일한 것, 생략 될 수있다.

make([]T, len)
make([]T, len, cap) // same as make([]T, cap)[:len]

아래쪽에, 익명의 배열 변수를 작성하게 한 다음 조각을 반환 익명으로 슬라이스를 반환하여 기본 배열 변수에 대한 참조 만. 첫 번째 문장에서, 슬라이스는 전체 어레이의 도면이다. 제 성명, 슬라이스 만 배열의 기초가되는 제 LEN 요소를 지칭하지만, 용량은 전체 어레이를 포함 할 것이다. 추가 요소는 미래 성장을 위해 예약되어 있습니다.

확장의 원리를 슬라이스

의 소스 코드는 확장의 원리를 분석하는 데에 기록 된 슬라이스하는 방법을 살펴 보자 :

func growslice(et *_type, old slice, cap int) slice {
    if et.size == 0 {
        if cap < old.cap {
            panic(errorString("growslice: cap out of range"))
        }
        // append should not create a slice with nil pointer but non-zero len.
        // We assume that append doesn't need to preserve old.array in this case.
        return slice{unsafe.Pointer(&zerobase), old.len, cap}
    }

    newcap := old.cap
    doublecap := newcap + newcap
    if cap > doublecap {
        newcap = cap
    } else {
        // 小于1024,*2扩容
        if old.len < 1024 {
            newcap = doublecap
        } else {
            // 大于1024,*1.25
            for newcap < cap {
                newcap += newcap / 4
            }
        }
    }
  // 下面代码省略
  ....
}

위의 코드에서 볼 수 있듯이, 슬라이스 확장 규칙은 다음과 같습니다

  1. 각 확장 oldCap * 2 = 1,024 미만 후, 캡
  2. 1024 이후, 각 확장 캡 = oldCap * 1.25

우리는 우리가 슬라이스를 만들 때 일반적으로,이 기능을 사용하는 것이 좋습니다, 추가 비용을 같은 시스템을 가져올 것이다, 모든 확장 배열의 복사본을 포함 볼, 다음 (새 배열에 슬라이스 포인트) 새로운 배열을 생성 할 수 있습니다 , 비즈니스 시나리오에 더 적합한 캡 크기 주어진 발생할 사본으로 기본 배열 조각의 확장을 방지 할 수 있습니다.

메모리 팁의 조각

사례 I :

입출력 슬라이스 주 다른 배열을 할당 방지 바닥 슬라이스 배열되지만 원래의 데이터가 덮어 쓰기 될 수있다 : 예를 들어

func nonempty(strings []string) []string {
    i := 0
    for _, s := range strings {
        if s != "" {
            strings[i] = s
            i++
        }
    }
    return strings[:i]
}

// 输出:
data := []string{"one", "", "three"}
fmt.Printf("%q\n", nonempty(data)) // `["one" "three"]`
fmt.Printf("%q\n", data)           // `["one" "three" "three"]`

마찬가지로, APPEND을 사용하여 동일한 기능을 달성 할 수있다 :

func nonempty2(strings []string) []string {
    out := strings[:0] // zero-length slice of original
    for _, s := range strings {
        if s != "" {
            out = append(out, s)
        }
    }
    return out
}

어쨌든 일반적으로 각각의 입력 값을 출력 값을 생성하기 위해 가입이 필요 이러한 많은 알고리즘들이 사용되거나 결합 필터 소자 인접 서열되는 사실 슬라이스를 재사용하는 방식으로 구현 하였다.

이 슬라이스의 사용은 몇 가지 기술은 슬라이스 있지만, 더 복잡한 기술이지만, 일부 응용 프로그램 상대적으로 명확하고 효과적입니다.

사례 II :

슬라이스가 스택을 통해, 수축 슬라이스 요소를 추가, 동작을 시뮬레이션 즉 슬라이스 스택에서 사용하는 스택을 팝 스택 요소 :

// 入栈, push
stack = append(stack, v)

// 出栈, pop
stack = stack[:len(stack)-1]

사례 3 :

슬라이스의 중간에 요소를 삭제하고 요소의 원래 순서가 내장 될 수있는 이동 뒤에 복사 기능 서브 슬라이스 앞으로 완료하기 위해 저장하려면 :

list := []int{1,2,3,4,5,6}
// 删除元素i,并保留原来的顺序,原理就是将i后面的元素按次序copy
copy(list[i:],list[i+1:])

마지막 요소 제거, 우리는 단순히 요소를 커버 할 수있는 원래의 질서를 유지하지 않고 요소를 삭제하려면 :

func remove(slice []int, i int) []int {
    // 使用最后一个元素覆盖要删除的元素
    slice[i] = slice[len(slice)-1]
    // 返回新的slice
    return slice[:len(slice)-1]
}

추천

출처www.cnblogs.com/vinsent/p/11326417.html