Go언어에는 상수 시간에 키의 존재를 확인할 수 있는 집합은 따로 제공하고 있지 않습니다. 여기서 가장 단순한 방법은 맵을 이용하면서 값을 bool형으로 주는 것입니다. 문자열에 중복되어 들어가 있는 글자가 있는지 검사하는 함수를 만드는 예제입니다.

package main


import "fmt"

func hasDupeRune(s string) bool {
	runeSet := map[rune]bool{}
	for _, r := range s {
		if runeSet[r] {
			return true
		}
		runeSet[r] = true
	}
	return false
}


func main() {
	test := "가나다라"
	fmt.Println(hasDupeRune(test))


	test1 := "가나다라가"
	fmt.Println(hasDupeRune(test1))
}


# 결과
false
true


코드는 깔끔하지만 불필요한 bool값 때문에 메모리를 차지한다는 단점이 있습니다. 이것을 해결하려면 빈 구조체를 값으로 사용하면 됩니다. 값 부분의 메모리를 따로 차지하지 않게 되어 있기 때문에 오버헤드가 존재하지 않습니다. 

여기서 문제는 특정 값이 있는지 없는지를 어떻게 아는지 입니다. 키가 있거나 없거나 맵에서 읽으면 빈 구조체가 나오기 때문입니다. 맵을 읽을 때 값을 2개로 받을 수 있습니다. 그때 두 번째 반환값을 조사하면 키가 있는지 없는지 알 수 있습니다. 따라서 위의 코드를 다음과 같이 변경할 수 있습니다.

package main


import "fmt"

func hasDupeRune(s string) bool {
	runeSet := map[rune]struct{}{}
	for _, r := range s {
		if _, exists := runeSet[r]; exists {
			return true
		}
		runeSet[r] = struct{}{}
	}
	return false
}

func main() {
	test := "가나다라"
	fmt.Println(hasDupeRune(test))
	test1 := "가나다라가"
	fmt.Println(hasDupeRune(test1))
}


struct부분에 대괄호를 열고 닫는 것이 두번 반복하니 이상해 보입니다. 일단 struct{}는 아무 필드가 없는 구조체이므로 값이 아니라 자료형입니다. 맵을 만들 때 맵의 자료형은 map[rune]struct가 되는 것이 아니라 map[rune]struct{}가 되므로 이 맵에 아무것도 안 들어 있는 상태로 초기화를 하려면 위와 같이 map[rune]struct{}{}를 해주어야 하는 것입니다. 그리고 다시 말해 struct{}는 빈 구조체 자료형이므로 해당 구조체에 아무 내용이 없는 상태의 값을 표현하려면 struct{}{}와 같이 표현을 해서 runeSet[r]=struct{}{}와 같이 써야 합니다.


맵과 집합에서 다루지 않은 하나가 더 있는데 삭제하는 방법입니다.

delete(m, key)

이렇게 하면 m[key]=기본값과 달리 아예 키가 사라지게 됩니다. 원래 맵에 키가 들어 있었으면 len(m)의 값이 1 감소하게 됩니다.

+ Recent posts