"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > Go의 지도

Go의 지도

2024-08-24에 게시됨
검색:956

Maps in Go

소개

Go에는 맵이라는 해시 테이블을 구현하는 기본 유형이 통합되어 있습니다. 이는 고유 키 모음과 각 키에 대한 값 모음으로 구성된 데이터 유형입니다.
예를 들어 키-값 쌍을 저장하는 다른 언어의 사전과 비교할 수 있습니다. 이러한 값은 이전 게시물에서 본 배열 및 슬라이스와 동일한 방식으로 키를 사용하여 액세스됩니다.
인덱스는 배열이나 슬라이스처럼 숫자로 제한되지 않으며 요소는 순서가 지정되지 않습니다. 따라서 맵을 인쇄하면 인쇄를 재정의하고 원하는 순서를 강제하기 위해 아무 것도 하지 않으면 임의의 순서가 반환됩니다.

지도 선언 및 초기화

맵을 선언하려면 map[key]값을 사용하면 됩니다. 여기서 key는 우리가 원하는 키 유형입니다(비교 가능한 유형이어야 합니다 https://go.dev/ref/spec#Comparison_operators). ) 그리고 값은 int에서 구조체까지, 또는 다른 맵까지, 유형이 무엇이든, 우리가 원하는 대로 맵이 각 키에 저장되기를 원하는 유형이 될 것입니다.

슬라이스와 마찬가지로 맵은 참조 유형이므로 맵의 0 값은 nil이 됩니다.
이는 그 아래에 키와 값을 저장하는 해시 테이블이 있고 그것들은 단순히 그것들의 추상화인 봉투이기 때문에 발생합니다.

다음과 같이 선언하면:

var m map[int]int

값은 nil이 됩니다.

0 값을 가지려면 다음 선언을 사용할 수 있습니다.

m := map[int]int{}

그리고 make 함수를 사용하여 슬라이스처럼 초기화할 수도 있습니다.

m := make(map[string]string)

이렇게 하면 적절한 메모리 풀로 해시 맵이 초기화되어 해당 데이터 구조를 가리키는 맵이 반환됩니다.

지도에서 값 추가 및 읽기

맵에 값을 추가하는 것은 배열이나 슬라이스와 마찬가지로 중괄호 []와 중괄호를 사용하여 수행됩니다. 이 예에서는 이름과 나이를 저장하기 위해 키가 문자열이고 값이 정수인 맵을 생성합니다.

ages := make(map[string]int)

ages["John"] = 33
ages["Charly"] = 27
ages["Jenny"] = 45
ages["Lisa"] = 19

맵을 선언할 때 여기에 값을 추가하려면 짧은 선언을 사용하고 모든 작업을 동일한 단계에서 수행할 수 있습니다.

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}

값을 읽으려면 지도에 키를 표시하기만 하면 해당 값이 반환됩니다. 예를 들어 Lisa의 나이를 알아내려면 다음을 수행할 수 있습니다.

fmt.Println(ages["Lisa"]) // 19

존재하지 않는 키에 액세스하려고 하면 얻은 값은 해당 유형의 0 값이 됩니다. 이 경우 문자열이므로 ""가 됩니다.

맵에 요소가 존재하는지 확인하기 위해 유형이 기본값인지 확인할 수 있지만 신뢰도가 높지는 않습니다. 요소가 존재하지만 해당 값이 빈 문자열이거나 int의 경우 0이기 때문입니다. , 이는 0 값과 일치하므로 Go는 다음을 수행하는 데 도움이 됩니다.

val, ok := ages["Randy"]

맵을 두 값과 동일하게 하면 첫 번째 값은 키를 통해 액세스된 해당 요소의 값(이 경우 존재하지 않는 "Randy")이고 두 번째 값은 부울 값이 됩니다. 존재하는지 존재하지 않는지.

값에 관심이 없고 단순히 키의 존재만 확인하려는 경우 다음과 같이 _를 사용하여 값을 무시할 수 있습니다.

_, ok := ages["Randy"]

배열 및 슬라이스와 마찬가지로 len 함수를 사용하여 맵에 몇 개의 요소가 있는지 확인할 수 있습니다.

fmt.Println(len(ages)) // 4

값을 수정하려면 키를 사용하여 해당 값에 액세스하고 다른 값과 일치시키면 수정되기만 하면 됩니다.

첫 번째 맵을 가리키는 두 번째 맵을 선언하고 두 번째 맵의 값을 수정하면 참조 유형이므로 첫 번째 맵의 값이 수정됩니다. 두 맵 모두 아래에서 동일한 해시 테이블을 공유하기 때문입니다.

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
agesNew := ages
agesNew["Bryan"] = 77
fmt.Println(agesNew) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]
fmt.Println(ages) // map[Bryan:77 Charly:27 Jenny:45 John:33 Lisa:19]

맵에서 값 제거

맵에서 요소를 삭제하기 위해 Go는 삭제될 맵과 키를 받는 다음 시그니처 delete(m map[Type]Type1, key Type)를 사용하여 삭제 함수를 제공합니다.
이전 사례에서 "Lisa"를 제거하려면 다음과 같이 하십시오.

delete(ages, "Lisa")

지도를 통해 반복

맵의 내용을 살펴보고 싶다면 배열과 슬라이스에 대한 게시물에서 이미 본 범위 변형과 함께 for를 사용하여 수행할 수 있습니다.
그러면 첫 번째 요소는 인덱스(즉, 키)가 되고 두 번째 요소는 값이 됩니다.

for key, value := range ages {
    fmt.Printf("%s: %d\n", key, value)
}

// Output:
// Jenny: 45
// Lisa: 19
// John: 33
// Charly: 27

배열 및 슬라이스와 마찬가지로 키 없이 값에만 관심이 있는 경우 _를 사용하여 생략할 수 있습니다.

for _, value := range ages {
    fmt.Println(value)
}

// Output:
// 19
// 33
// 27
// 45

그리고 우리가 관심을 갖는 것이 단순히 키라면 범위를 단일 변수에 할당하여 키를 얻을 수 있습니다.

for key := range ages {
    fmt.Println(key)
}

// Output:
// John
// Charly
// Jenny
// Lisa

지도 정렬

소개에서 언급했듯이 지도에서는 ​​정보의 순서가 지정되지 않으므로 이를 반복할 때 어떤 순서를 따르는지 지정할 수 없으며 Go에서는 실행 간의 순서가 동일하다고 보장할 수 없습니다.
배열과 슬라이스에서 본 것처럼 표준 라이브러리에는 요소 정렬을 돕는 정렬 패키지가 있습니다: https://pkg.go.dev/sort

연령에 대한 예제를 따르고 sort를 사용하면 지도를 탐색하기 전에 지도의 키를 정렬할 수 있으므로 순서대로 액세스할 수 있습니다.

ages := map[string]int{"John": 33, "Charly": 27, "Jenny": 45, "Lisa": 19}
keys := make([]string, 0, len(ages))
for k := range ages {
    keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
    fmt.Println(k, ages[k])
}

// Output:
// Charly 27
// Jenny 45
// John 33
// Lisa 19

앞서 본 것처럼 짧은 선언으로 연령 지도를 선언합니다.
현재 키가 없기 때문에 키를 저장하기 위해 문자열 슬라이스를 만들고 길이가 0인 make 메소드를 사용하지만 맵 길이에 대해 len 메소드를 사용하여 용량을 예약합니다. &&&] age 맵을 검토하여 키를 유지하고 생성된 슬라이스에 추가합니다.
sort.Strings 함수를 사용하여 키를 알파벳순으로 정렬합니다.
우리는 이미 주문한 키 조각을 살펴보고 문제의 키가 있는 지도에 액세스합니다.
이런 식으로 우리는 순서대로 지도에 접근할 수 있고 프로그램에 필요한 논리를 수행할 수 있습니다.

동시성 문제

지도와 관련하여 염두에 두어야 할 점은 동시에 사용하는 것이 안전하지 않다는 것입니다. 값에 액세스하거나 범위가 있는 for를 통해 동시 읽기인 경우 여러 고루틴이 동시에 액세스하는 데 문제가 없습니다.

문제가 되는 경우는 맵에 요소를 추가하거나 제거하여 맵의 값을 동시에 업데이트하려는 동시에 다른 쪽에서 맵을 읽는 경우입니다.
이 상황을 해결하기 위한 몇 가지 가능한 해결책이 있습니다. 자세한 내용은 다루지 않고 간단히 언급하고 더 자세히 알아보는 것은 여러분의 선택에 맡기겠습니다.

표준 라이브러리의 동기화 패키지(https://pkg.go.dev/sync)를 사용하면 서로 다른 고루틴 간의 동기화를 제어할 수 있습니다.

가능한 용도는 유형에 대한 읽기 및 쓰기를 잠그거나 잠금 해제할 수 있는 RWMutex 유형입니다. 따라서 sync.RWMutex와 맵을 포함하는 유형이 있으면 액세스할 수 있는 시기를 제어할 수 있습니다.
동일한 동기화 패키지 내에서 조사할 또 다른 흥미로운 유형은 Map입니다. 이는 이미 지도 작업에 도움이 되는 일련의 기능을 제공하지만 결국 이전 솔루션과 마찬가지로 기본적으로 작업할 수 없게 됩니다.
우리가 구현하는 사용 사례에 따라 둘 중 하나가 우리에게 더 유용할 것이며 다른 것보다 더 나은 것은 없으며 항상 필요한 것에 따라 달라집니다.

이번 포스팅에서 제가 설명하려고 했던 내용이 모두 이해가 되었기를 바라며, 아직 완전히 명확하지 않은 부분이나 아직 다루지 못한 부분이 있다면 남겨주시길 바랍니다. 여기 또는 내 프로필에 있는 소셜 네트워크를 통해 댓글을 남겨 주시면 기꺼이 응답해 드리겠습니다.

릴리스 선언문 이 글은 https://dev.to/charly3pins/maps-in-go-5a3j?1 에서 복제되었습니다. 침해 내용이 있는 경우, [email protected]으로 연락하여 삭제하시기 바랍니다.
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3