본문 바로가기
golang (go)

golang sync.Map range (Iteration)

by first author 2024. 10. 11.
728x90
반응형

golang 에서는 반복문을 이용하여 array, slice, map 을 손쉽게 iteration을 수행할 수 있도록 for range을 제공하고 있다.

for fruitsName, price := range fruitsPrice {
  fmt.Println("과일 이름:", fruitsName, "-> 가격", price)
}

 

하지만 Thread Safety 한 sync.Map 은 for range를 이용하여 Iteration을 수행할 수 없고, sync.Map 에 구현되어있는 Range를 구현하여 Iteration을 수행할 수 있다.

func (m *Map) Range(f func(key, value any) bool)

 

sync.Map 의 Range에서 제공하는 key, value 은 모두 any 타입 입니다.

any는 interface{} 와 동일하며, 구조체 또는 golang Types에서 제공하는 모든 타입으로 변환이 가능하다. 

type any = interface{}

 

따라서 key, value를 특정 타입으로 변환하기 위해서는 type assertion을 수행해야한다.

type conversion과 assertion의 차이는 이전에 작성한 블로그를 참조하기 바란다.

 

만약 위에서 정의한 fruitsPrice가 sync.Map으로 구성되어있다면 아래와 같이 Range()를 구현하여 Iteration을 수행하면 된다. 

fruitsPrice.Range(func(k, v any) bool {
  name := k.(string)
  price := v.(uint64)
  fmt.Printf("과일 이름: %s, 가격: %d\n", name, price) 
  return true
}

 

Range() 를 구현할 때 return 값으로 true/false를 이용하여 iteration을 지속할 것인지 중단할 것인지 결정하면 된다.

만약 iteration을 지속적으로 수행해야하는 경우 true를, 멈추기 위해서는 false를 return 하면 된다.

 

반응형
728x90

 

Range()의 가장 큰 장점으로는 Range를 수행하는 중에 Element가 삭제되거나 추가된 결과가 그대로 반영된다는 것이다.

물론 이미 Range를 수행한 Element가 지워진 경우 이미 수행한 Range 동작 결과가 변하는 것은 아니지만 아직 Visited 되지 않은 경우 중간에 변경된 결과가 그대로 반영된다.

 

아래 예제 코드를 보면 Range 를 수행하는 도중 Delete가 이루어졌고, 해당 Element는 Range에서 접근하기 전이였다면 그대로 Delete가 반영되어 Range 내에서는 Visited 되지 않는 것을 확인할 수 있다. 

 

Range 순회 중 Delete 수행 예제 코드:

fruitsPrice := sync.Map{}
wg := sync.WaitGroup{}

fruitsPrice.Store("apple", 1000)
fruitsPrice.Store("banana", 500)
fruitsPrice.Store("orange", 2000)
fruitsPrice.Store("grape", 3000)
fruitsPrice.Store("kiwi", 1500)
fruitsPrice.Store("mango", 2500)
fruitsPrice.Store("melon", 3500)
fruitsPrice.Store("peach", 4000)
fruitsPrice.Store("pear", 4500)
fruitsPrice.Store("plum", 5000)

wg.Add(1)
go func() {
	defer wg.Done()
	fruitsPrice.Range(func(k, v any) bool {
		name := k.(string)
		price := v.(int)
		fmt.Printf("fruit name: %s, price: %d\n", name, price)
		return true
	})
}()

wg.Add(1)
go func() {
	defer wg.Done()
	if price, ok := fruitsPrice.LoadAndDelete("mango"); ok {
		fmt.Printf("delete mango price: %d\n", price)
	}
}()

wg.Add(1)
go func() {
	defer wg.Done()
	if price, ok := fruitsPrice.LoadAndDelete("pear"); ok {
		fmt.Printf("delete pear price: %d\n", price)
	}
}()


wg.Wait()

 

Range 순회 중 Delete 한 결과 출력: 

delete mango price: 2500
fruit name: orange, price: 2000
fruit name: melon, price: 3500
fruit name: plum, price: 5000
fruit name: banana, price: 500
delete pear price: 4500
fruit name: grape, price: 3000
fruit name: kiwi, price: 1500
fruit name: peach, price: 4000
fruit name: apple, price: 1000

 

Range 중 Insert, Delete가 자유롭다는 장점도 있지만 반대로 Lock이 되지 않는다는 것으로 이용 시 주의해야 한다. 

 

2023.12.24 - [golang (go)] - golang map

 

golang map

Go Map Introduction Golang 에서 map은 Hash table을 기반으로 구현되어있다. 이에 빠른 검색, 추가, 삭제를 특징으로 한다. Hash Table 기본적으로 Hash Collision이 없다는 전재하에 Olog1의 매우 빠른 검색 속도를

takeanoteof.tistory.com

 

2024.01.14 - [golang (go)] - golang slice elements copy (copy(), append(), for ... range)

 

golang slice elements copy (copy(), append(), for ... range)

golang에서 slice는 특정 배열의 위치를 가르키는 pointer를 가지는 reference 타입의 구조체이다. 그렇기 때문에 대입 연산자 ('=') 를 사용하여 Copy를 하면 값(Elements) 가 Copy가 되는 것이 아니라  referenc

takeanoteof.tistory.com

 

2023.02.03 - [golang (go)] - golang type conversion [string(val)], type assertion [val.(string)]

 

golang type conversion [string(val)], type assertion [val.(string)]

golang은 두가지 타입의 형 변환 방법이 있다. 하나는 변수의 타입을 변환해주기 위한, 즉 형 변환을 위한 type conversion 과 임의의 값을 가지는 interface를 다시 임의의 타입으로 변환하기 위한 type ass

takeanoteof.tistory.com

 

728x90
반응형

댓글