声明 map 的方式有点儿类似于数组。不同之处是,它以 map 关键字开头,需要两种类型。第一个是键的类型,写在 [] 中。第二个是值的类型,跟在 [] 之后。
键的类型很特别,它只能是一个可比较的类型,因为如果不能判断两个键是否相等,我们就无法确保我们得到的是正确的值。
另一方面,值的类型可以是任意类型,它甚至可以是另一个 map。
Error 类型可以使用 .Error() 方法转换为字符串。
Map 有一个有趣的特性,不使用指针传递你就可以修改它们。这是因为 map 是引用类型。这意味着它拥有对底层数据结构的引用,就像指针一样。它底层的数据结构是 hash table 或 hash map。
Map 作为引用类型是非常好的,因为无论 map 有多大,都只会有一个副本。
如果值已存在,map 不会抛出错误。相反,它们将继续并使用新提供的值覆盖该值。
引用类型
Map 有一个有趣的特性,不使用指针传递你就可以修改它们。这是因为 map
是引用类型。这意味着它拥有对底层数据结构的引用,就像指针一样。它底层的数据结构是 hash table
或 hash map
,你可以在这里阅读有关 hash tables
的更多信息。
Map 作为引用类型是非常好的,因为无论 map 有多大,都只会有一个副本。
引用类型引入了 maps
可以是 nil
值。如果你尝试使用一个 nil
的 map,你会得到一个 nil 指针异常
,这将导致程序终止运行。
由于 nil 指针异常
,你永远不应该初始化一个空的 map 变量:
var m map[string]string
相反,你可以像我们上面那样初始化空 map,或使用 make
关键字创建 map:
dictionary = map[string]string{}
// OR
dictionary = make(map[string]string)
这两种方法都可以创建一个空的 hash map
并指向 dictionary
。这确保永远不会获得 nil 指针异常
。
package main
const (
//ErrNotFound means the definition could not be found for the given word
ErrNotFound = DictionaryErr("could not find the word you were looking for")
//ErrWordExists means you are trying to add a word that is already known
ErrWordExists = DictionaryErr("cannot add word because it already exists")
//ErrWordDoesNotExist occurs when trying to update a word not in the dictionary
ErrWordDoesNotExist = DictionaryErr("cannot update word because it does not exist")
)
//自定义错误类型
type DictionaryErr string
func (e DictionaryErr) Error() string {
return string(e)
}
//Dictionary store definitions to words
type Dictionary map[string]string
//Search find a word in the dictionary
func (d Dictionary) Search(word string) (string, error) {
definition, ok := d[word]
if !ok {
return "", ErrNotFound
}
return definition, nil
}
// Add inserts a word and definition into the dictionary
func (d Dictionary) Add(word, definition string) error {
_, err := d.Search(word)
switch err {
case ErrNotFound:
d[word] = definition
case nil:
return ErrWordExists
default:
return err
}
return nil
}
//Update changes the definition of a given word
func (d Dictionary) Update(word, definition string) error {
_, err := d.Search(word)
switch err {
case ErrNotFound:
return ErrWordDoesNotExist
case nil:
d[word] = definition
default:
return err
}
return nil
}
// Delete removes a word from the dictionary
func (d Dictionary) Delete(word string) {
delete(d, word)
}
package main
import (
"testing"
)
func TestSearch(t *testing.T) {
dictionary := Dictionary{"test": "this is just a test"}
t.Run("known word", func(t *testing.T) {
got, _ := dictionary.Search("test")
want := "this is just a test"
assertStrings(t, got, want)
})
t.Run("unknown word", func(t *testing.T) {
_, got := dictionary.Search("unknown")
assertError(t, got, ErrNotFound)
})
}
func TestAdd(t *testing.T) {
t.Run("new word", func(t *testing.T) {
dictionary := Dictionary{}
word := "test"
definition := "this is just a test"
err := dictionary.Add(word, definition)
assertError(t, err, nil)
assertDefinition(t, dictionary, word, definition)
})
t.Run("existing word", func(t *testing.T) {
word := "test"
definition := "this is just a test"
dictionary := Dictionary{word: definition}
err := dictionary.Add(word, "new test")
assertError(t, err, ErrWordExists)
assertDefinition(t, dictionary, word, definition)
})
}
func TestUpdate(t *testing.T) {
t.Run("existing word", func(t *testing.T) {
word := "test"
definition := "this is just a test"
newDefinition := "new definition"
dictionary := Dictionary{word: definition}
err := dictionary.Update(word, newDefinition)
assertError(t, err, nil)
assertDefinition(t, dictionary, word, newDefinition)
})
t.Run("new word", func(t *testing.T) {
word := "test"
definition := "this is just a test"
dictionary := Dictionary{}
err := dictionary.Update(word, definition)
assertError(t, err, ErrWordDoesNotExist)
})
}
func TestDelete(t *testing.T) {
word := "test"
dictionary := Dictionary{word: "test definition"}
dictionary.Delete(word)
_, err := dictionary.Search(word)
if err != ErrNotFound {
t.Errorf("Expected '%s' to be deleted", word)
}
}
func assertStrings(t *testing.T, got, want string) {
t.Helper()
if got != want {
t.Errorf("got '%s' want '%s'", got, want)
}
}
func assertError(t *testing.T, got, want error) {
t.Helper()
if got != want {
t.Errorf("got error '%s' want '%s'", got, want)
}
}
func assertDefinition(t *testing.T, dictionary Dictionary, word, definition string) {
t.Helper()
got, err := dictionary.Search(word)
if err != nil {
t.Fatal("should find added word:", err)
}
if definition != got {
t.Errorf("got '%s' want '%s'", got, definition)
}
}
网友评论