지도에서 키 조각 가져 오기


230

Go의지도에서 키 조각을 가져 오는 더 간단하고 효율적인 방법이 있습니까?

현재지도를 반복하고 키를 슬라이스에 복사하고 있습니다.

i := 0
keys := make([]int, len(mymap))
for k := range mymap {
    keys[i] = k
    i++
}

19
정답은 '아니오'입니다. 더 간단하고 더 좋은 방법은 없습니다.
dldnh

답변:


202

예를 들어

package main

func main() {
    mymap := make(map[int]string)
    keys := make([]int, 0, len(mymap))
    for k := range mymap {
        keys = append(keys, k)
    }
}

Go에서 효율적으로 사용하려면 메모리 할당을 최소화하는 것이 중요합니다.


29
용량 대신 실제 크기를 설정하고 추가하지 않는 것이 좋습니다. 자세한 내용은 내 답변을 참조하십시오.
Vinay Pai

3
참고 경우 mymap로컬 변수 아닌 (따라서 / 성장 감소의 대상이다), 이것이 유일한 적절한 해결책이다 - 그것은 크기 경우되도록 mymap초기화 간의 변경 keysfor루프 임의 OUT- 없을 것 경계 문제.
Melllvar

12
동시에 액세스 할 경우 맵이 안전하지 않으며 다른 goroutine이 맵을 변경할 수있는 경우 해결책이 없습니다.
Vinay Pai

@VinayPai 여러 goroutines에서지도에서 읽을 수 있지만 쓸 수 없습니다
darethas

@darethas는 일반적인 오해입니다. 레이스 탐지기는 1.6 이후로이 사용법을 표시합니다. 릴리스 노트에서 : "항상, 한 고 루틴이 맵에 쓰는 경우 다른 고 루틴이 동시에 맵을 읽거나 쓰지 않아야합니다. 런타임이이 조건을 감지하면 진단을 인쇄하고 프로그램을 중단시킵니다." golang.org/doc/go1.6#runtime
Vinay Pai

375

이것은 오래된 질문이지만 여기에 2 센트가 있습니다. PeterSO의 대답은 약간 간결하지만 약간 덜 효율적입니다. 이미 얼마나 큰지 알고 있으므로 append를 사용할 필요조차 없습니다.

keys := make([]int, len(mymap))

i := 0
for k := range mymap {
    keys[i] = k
    i++
}

대부분의 상황에서 아마도 큰 차이는 없지만 아마도 더 많은 작업은 아니며 내 테스트 (무작위 1,000,000 개의 int64키가 있는 맵을 사용한 다음 각 방법으로 키 배열을 10 번 생성)에서 append를 사용하는 것보다 직접 배열 멤버를 할당하는 데 20 % 빠릅니다.

용량을 설정하면 재 할당이 제거되지만 append는 여전히 각 추가 용량에 도달했는지 확인하기 위해 추가 작업을 수행해야합니다.


46
이것은 OP 코드와 정확히 동일합니다. 이것이 더 좋은 방법이라는 데 동의하지만이 답변의 코드와 OP 코드의 차이점을 놓친 경우 궁금합니다.
Emmaly Wilson

4
좋은 지적, 나는 어떻게 든 다른 답변을보고 내 답변이 OP와 정확히 동일하다는 것을 놓쳤다. 아, 적어도 우리는 이제 불필요하게 append를 사용하는 것에 대한 벌칙이 무엇인지 대략 알고 있습니다 :)
Vinay Pai

5
왜, 범위와 인덱스를 사용하지 않습니다 for i, k := range mymap{. 그렇게하면 i ++이 필요하지 않습니까?
mvndaai

30
어쩌면 내가 여기서 뭔가를 실종 해요,하지만 당신이 한 경우 i, k := range mymap, 다음 i키되며 k맵에서 그 키에 해당하는 값이 될 것입니다. 실제로 키 조각을 채우는 데 도움이되지는 않습니다.
Vinay Pai

4
@Alaska 하나의 임시 카운터 변수를 할당하는 비용에 관심이 있지만 함수 호출이 메모리를 덜 차지한다고 생각하면 함수가 호출 될 때 실제로 발생하는 것에 대해 스스로 교육해야합니다. 힌트 : 무료로 일을하는 것은 마법의 주문이 아닙니다. 현재 허용되는 답변이 동시 액세스에서 안전하다고 생각되면 기본 사항 ( blog.golang.org/go-maps-in-action#TOC_6) 으로 돌아 가야 합니다.
Vinay Pai

79

패키지 "reflect"에서 struct의 []Value방법 MapKeys에 따라 유형이있는 키 배열을 가져올 수도 있습니다 Value.

package main

import (
    "fmt"
    "reflect"
)

func main() {
    abc := map[string]int{
        "a": 1,
        "b": 2,
        "c": 3,
    }

    keys := reflect.ValueOf(abc).MapKeys()

    fmt.Println(keys) // [a b c]
}

1
필자는 동시 맵 액세스의 가능성이있는 경우 이것이 좋은 접근 방법이라고 생각합니다. 루프 중에 맵이 커지면 당황하지 않습니다. 성능에 대해서는 확실하지 않지만 추가 솔루션보다 성능이 뛰어나다 고 생각합니다.
Atila Romero

@AtilaRomero이 솔루션에 어떤 장점이 있는지 확실하지는 않지만 어떤 목적으로도 리플렉션을 사용하는 경우 키를 직접 입력 한 값으로 사용할 수 있으므로 더 유용합니다.
Denis Kreshikhin

10
이것을로 변환하는 방법이 []string있습니까?
Doron Behar

14

이 작업을 수행하는 더 좋은 방법은 다음을 사용하는 것입니다 append.

keys = []int{}
for k := range mymap {
    keys = append(keys, k)
}

그 외에는 운이 좋지 않습니다. Go는 표현력이 뛰어난 언어가 아닙니다.


10
원본보다 효율적이지 않습니다-append는 기본 배열을 늘리기 위해 여러 할당을 수행하며 모든 호출에서 슬라이스 길이를 업데이트해야합니다. 말하는 keys = make([]int, 0, len(mymap))것은 할당을 제거 할 것이지만 여전히 느릴 것으로 예상합니다.
Nick Craig-Wood

1
이 답변은 사본을 만드는 동안 다른 사람이지도를 변경하는 경우 len (mymap)을 사용하는 것보다 안전합니다.
Atila Romero

7

다른 답변에서 설명한 세 가지 방법에 대한 스케치 벤치 마크를 만들었습니다.

키를 당기기 전에 슬라이스를 미리 할당하는 것이 appending 보다 빠르지 만 놀랍게도이 reflect.ValueOf(m).MapKeys()방법은 후자보다 훨씬 느립니다.

 go run scratch.go
populating
filling 100000000 slots
done in 56.630774791s
running prealloc
took: 9.989049786s
running append
took: 18.948676741s
running reflect
took: 25.50070649s

여기에 코드입니다 : https://play.golang.org/p/Z8O6a2jyfTH (. 너무 오래, 너무 잘 로컬로 실행 걸립니다 주장 놀이터 중단한다에서 실행이)


2
당신의에서 keysAppend기능, 당신은의 용량을 설정할 수 keys와 배열 make([]uint64, 0, len(m))대폭 나를 위해 그 기능의 성능을 변경.
keithbhunter

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.