Немного лучше установить фактический размер вместо емкости и избегать добавления в целом. Смотрите мой ответ для деталей.
Винай Пай
3
Обратите внимание, что если mymapне локальная переменная (и, следовательно, подвержена росту / уменьшению), это единственное правильное решение - оно гарантирует, что если размер mymapизменений между инициализацией keysи forциклом не будет, о проблемах.
Меллвар
12
карты не безопасны при одновременном доступе, и ни одно из решений не является приемлемым, если другая программа может изменить карту.
Винай Пай
@VinayPai можно читать с карты из нескольких программ, но не писать
см.
@darethas, это распространенное заблуждение. Детектор гонки помечает это использование с 1.6. Из примечаний к выпуску: «Как всегда, если одна программа записывает данные на карту, никакая другая программа не должна одновременно считывать или записывать карту. Если среда выполнения обнаруживает это условие, она печатает диагноз и вылетает из программы». golang.org/doc/go1.6#runtime
Винай Пай
374
Это старый вопрос, но вот мои два цента. Ответ PeterSO немного более краткий, но чуть менее эффективный. Вы уже знаете, насколько большим он будет, поэтому вам даже не нужно использовать append:
keys := make([]int, len(mymap))
i :=0for k := range mymap {
keys[i]= k
i++}
В большинстве ситуаций это, вероятно, не будет иметь большого значения, но это не намного больше работы, и в моих тестах (используя карту с 1 000 000 случайных int64ключей и затем генерируя массив ключей десять раз с каждым методом), это было примерно На 20% быстрее назначать члены массива напрямую, чем использовать append.
Несмотря на то, что установка емкости исключает перераспределение, добавлению все равно приходится выполнять дополнительную работу, чтобы проверить, достигли ли вы емкости для каждого добавления.
Это выглядит точно так же, как код ОП. Я согласен, что это лучший способ, но мне любопытно, если я пропустил разницу между кодом этого ответа и кодом OP.
Эммали Уилсон
4
Хороший вопрос, я как-то посмотрел на другие ответы и пропустил, что мой ответ точно такой же, как ОП. Ну что ж, по крайней мере, теперь мы примерно знаем, что такое штраф за ненужное добавление :)
Vinay Pai
5
Почему вы не используете индекс с диапазоном for i, k := range mymap{. Таким образом, вам не нужен i ++?
mvndaai
30
Может быть, я что-то здесь упускаю, но если вы это сделали i, k := range mymap, то iбудут ключи и kбудут значения, соответствующие этим ключам на карте. Это на самом деле не поможет вам заполнить кусок ключей.
Винай Пай
4
@ Аляска, если вас беспокоит стоимость выделения одной временной переменной-счетчика, но вы думаете, что вызов функции займет меньше памяти, вы должны узнать, что на самом деле происходит при вызове функции. Подсказка: это не волшебное заклинание, которое делает вещи бесплатно. Если вы считаете, что принятый в настоящее время ответ безопасен при одновременном доступе, вам также необходимо вернуться к основам: blog.golang.org/go-maps-in-action#TOC_6 .
Винай Пай
79
Вы также можете взять массив ключей с типом []Valueметодом MapKeysstruct Valueиз пакета «refle»:
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]}
Я думаю, что это хороший подход, если есть шанс одновременного доступа к карте: это не будет паниковать, если карта увеличивается во время цикла. Что касается производительности, я не совсем уверен, но я подозреваю, что она превосходит решение для добавления.
Атила Ромеро
@AtilaRomero Не уверен, что у этого решения есть какие-либо преимущества, но при использовании отражения для каких-либо целей это более полезно, поскольку оно позволяет получить ключ в качестве значения, введенного напрямую.
Денис Крешихин
10
Есть ли способ преобразовать это в []string?
Дорон Бехар
14
Лучший способ сделать это - использовать append:
keys =[]int{}for k := range mymap {
keys = append(keys, k)}
Кроме того, вам не повезло - иди не очень выразительный язык.
Это менее эффективно, чем оригинал - append будет делать несколько выделений для увеличения базового массива и должен обновлять длину среза при каждом вызове. Скажем keys = make([]int, 0, len(mymap)), избавится от распределений, но я ожидаю, что все равно будет медленнее.
Ник Крейг-Вуд
1
Этот ответ безопаснее, чем использование len (mymap), если кто-то еще меняет карту во время создания копии.
Атила Ромеро
7
Я сделал схематичный тест по трем методам, описанным в других ответах.
Очевидно, что предварительное выделение фрагмента перед нажатием клавиш происходит быстрее, чем appending, но, что удивительно, reflect.ValueOf(m).MapKeys()метод значительно медленнее последнего:
❯ go run scratch.go
populating
filling 100000000 slots
donein56.630774791s
running prealloc
took:9.989049786s
running append
took:18.948676741s
running reflect
took:25.50070649s
Вот код: https://play.golang.org/p/Z8O6a2jyfTH
(запуск его на детской площадке прерывается, утверждая, что это занимает слишком много времени, поэтому, хорошо, запустите его локально.)
В вашей keysAppendфункции вы можете установить емкостьkeys массива make([]uint64, 0, len(m)), что радикально изменило производительность этой функции для меня.
Ответы:
Например,
Чтобы быть эффективным в Go, важно минимизировать распределение памяти.
источник
mymap
не локальная переменная (и, следовательно, подвержена росту / уменьшению), это единственное правильное решение - оно гарантирует, что если размерmymap
изменений между инициализациейkeys
иfor
циклом не будет, о проблемах.Это старый вопрос, но вот мои два цента. Ответ PeterSO немного более краткий, но чуть менее эффективный. Вы уже знаете, насколько большим он будет, поэтому вам даже не нужно использовать append:
В большинстве ситуаций это, вероятно, не будет иметь большого значения, но это не намного больше работы, и в моих тестах (используя карту с 1 000 000 случайных
int64
ключей и затем генерируя массив ключей десять раз с каждым методом), это было примерно На 20% быстрее назначать члены массива напрямую, чем использовать append.Несмотря на то, что установка емкости исключает перераспределение, добавлению все равно приходится выполнять дополнительную работу, чтобы проверить, достигли ли вы емкости для каждого добавления.
источник
for i, k := range mymap{
. Таким образом, вам не нужен i ++?i, k := range mymap
, тоi
будут ключи иk
будут значения, соответствующие этим ключам на карте. Это на самом деле не поможет вам заполнить кусок ключей.Вы также можете взять массив ключей с типом
[]Value
методомMapKeys
structValue
из пакета «refle»:источник
[]string
?Лучший способ сделать это - использовать
append
:Кроме того, вам не повезло - иди не очень выразительный язык.
источник
keys = make([]int, 0, len(mymap))
, избавится от распределений, но я ожидаю, что все равно будет медленнее.Я сделал схематичный тест по трем методам, описанным в других ответах.
Очевидно, что предварительное выделение фрагмента перед нажатием клавиш происходит быстрее, чем
append
ing, но, что удивительно,reflect.ValueOf(m).MapKeys()
метод значительно медленнее последнего:Вот код: https://play.golang.org/p/Z8O6a2jyfTH (запуск его на детской площадке прерывается, утверждая, что это занимает слишком много времени, поэтому, хорошо, запустите его локально.)
источник
keysAppend
функции вы можете установить емкостьkeys
массиваmake([]uint64, 0, len(m))
, что радикально изменило производительность этой функции для меня.Посетите https://play.golang.org/p/dx6PTtuBXQW.
источник