[프렉티컬 C#] Dictionary
HashSet<T> 클래스
- Dictionary<TKey, TValue>와 비슷하지만 키 부분만 저장하고 값은 저장하지 않는다.
- 중복을 허용하지 않는다.
- 요소를 꺼내는 순서는 정해져 있지 않아서 등록한 순서로 나온다는 보장이 없다.
- Add 메서드는 요소가 객체에 추가되면 true를 반환하고 이미 존재하는 경우에는 예외를 발생시키지 않고 false를 반환한다.
Dictionary<TKey, TValue> 제네릭 클랙스
- 해시 테이블이라는 데이터 구조로 만들어진 클래스
- 키와 이 키에 대응하는 값을 여러 개 저장할 수 있는 컬렉션
- 배열이나 리스트와 비교했을 때 키를 사용해 값을 구하므로 속도가 가장 빠르다.
딕셔너리 초기화
var abcDic = new Dictionaray<string, int>() {
{ "abc", 400 },
{ "def", 300 },
{ "ghi", 350 },
{ "jkl", 500 },
{ "mno", 450 },
};
// C# 6.0 이후
var abcDic = new Dictionaray<string, int>() {
[ "abc"] = 400 ,
[ "def"] = 300 ,
[ "ghi"] = 350 ,
[ "jkl"] = 500 ,
[ "mno"] = 450 ,
};
사용자 정의형 객체를 값으로 지정하기
사원 코드를 키로 설정해 쉽고 빠르게 Employee 객체를 구할 수 있다.
var employeeDict = new Dictionaray<int, Employee> {
{ 100, new Employee(100, "김순자") },
{ 200, new Employee(200, "오달순") },
{ 300, new Employee(300, "이아주") },
};
딕션너리 요소 추가
// 1번
employeeDict[200] = new Employee(201, "강지순");
// 2번
employeeDict.Add( 200, new Employee(201, "강지순") );
딕션너리 요소 삭제
Remove 메서드
Dictionary<TKey,TValue>.Remove 메서드 (System.Collections.Generic)
Dictionary<TKey,TValue>에서 지정한 키가 있는 값을 제거합니다.
learn.microsoft.com
ContainsKey 메서드
Dictionary<TKey,TValue>에 지정한 키가 포함되어 있는지 여부를 확인합니다.
Dictionary<TKey,TValue>.ContainsKey(TKey) 메서드 (System.Collections.Generic)
Dictionary<TKey,TValue>에 지정한 키가 포함되어 있는지 여부를 확인합니다.
learn.microsoft.com
딕셔너리에 있는 모든 요소 꺼내기
- foreach문 사용
- foreach로 꺼낼 수 있는 요소의 형은 KeyValuepair<TKey, TValue>형이다.
- 요소를 꺼내는 순서는 정해져 있지 않아서 등록한 순서로 나온다는 보장이 없다.
foreach (var item in dict)
Console.WriteLine("{0} = {1}", item.Key, item.Value);
딕셔너리에 있는 모든 키 꺼내기
- Keys 속성 사용
- 요소를 꺼내는 순서는 정해져 있지 않아서 등록한 순서로 나온다는 보장이 없다.
foreach (var key in dict.Keys)
Console.WriteLine(key);
딕셔너리로 변환
ToDictionary 메서드
- IEnumerable<T>에서 Dictionary<TKey,TValue>을 만든다.
- 배열이나 리스트를 딕셔너리로 변환할 수 있다.
Enumerable.ToDictionary 메서드 (System.Linq)
IEnumerable<T>에서 Dictionary<TKey,TValue>을 만듭니다.
learn.microsoft.com
class Package
{
public string Company { get; set; }
public double Weight { get; set; }
public long TrackingNumber { get; set; }
}
public static void ToDictionaryEx1()
{
List<Package> packages =
new List<Package>
{ new Package { Company = "Coho Vineyard", Weight = 25.2, TrackingNumber = 89453312L },
new Package { Company = "Lucerne Publishing", Weight = 18.7, TrackingNumber = 89112755L },
new Package { Company = "Wingtip Toys", Weight = 6.0, TrackingNumber = 299456122L },
new Package { Company = "Adventure Works", Weight = 33.8, TrackingNumber = 4665518773L } };
// Create a Dictionary of Package objects,
// using TrackingNumber as the key.
Dictionary<long, Package> dictionary =
packages.ToDictionary(p => p.TrackingNumber);
foreach (KeyValuePair<long, Package> kvp in dictionary)
{
Console.WriteLine(
"Key {0}: {1}, {2} pounds",
kvp.Key,
kvp.Value.Company,
kvp.Value.Weight);
}
}
/*
This code produces the following output:
Key 89453312: Coho Vineyard, 25.2 pounds
Key 89112755: Lucerne Publishing, 18.7 pounds
Key 299456122: Wingtip Toys, 6 pounds
Key 4665518773: Adventure Works, 33.8 pounds
*/
딕셔너리로부터 다른 딕셔너리 생성
var abcDic = new Dictionaray<string, int>() {
[ "abc"] = 400 ,
[ "def"] = 300 ,
[ "ghi"] = 350 ,
[ "jkl"] = 500 ,
[ "mno"] = 450 ,
};
var newDic = abcDic.Where(x => x.Value >= 300)
.ToDictionaray(a => a.Key, a => a.Value); // 두번째 인수도 필요!
사용자 지정 클래스를 키로 사용하기
- Equals 메서드와 GetHashCode 메서드 오버라이드 해야한다.
‣ 솔직히 이해를 못하겠음 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ 누구 좋은 블로그 있어면 추천 좀 여...
- 오버라이드 안 하면 System.Collections.Generic.KeyNotFoundException 예외 발생한다.
class MonthDay
{
public int Day { get; private set; }
public int Month { get; private set; }
public MonthDay(int month, int day)
{
this.Month = month;
this.Day = day;
}
// Equals, GetHashCode 오버라이드 안하면 예외 발생한다.
// MonthDay끼리 비교한다
public override bool Equals(object obj)
{
var other = obj as MonthDay;
if (other == null)
throw new ArgumentException();
return this.Day == other.Day && this.Month == other.Month;
}
// 해시 코드를 구한다
public override int GetHashCode()
{
return Month.GetHashCode() * 31 + Day.GetHashCode();
}
}
var dict = new Dictionary<MonthDay, string> {
{ new MonthDay(6, 6), "현충일" },
{ new MonthDay(8, 15), "광복절" },
{ new MonthDay(10, 3), "개천절" },
};
var md = new MonthDay(8, 15);
var s = dict[md];
Console.WriteLine(s);
키 중복 허용하기
값의 형을 List<>로 지정한다. ➢ 하나의 키에 여러 값을 지정할 수 있게 된다. ➢ 결과적으로 키가 중복되는 것을 허용하는 것이다.
// 딕셔너리를 초기화한다
var dict = new Dictionary<string, List<string>>() {
{ "PC", new List<string> { "퍼스널 컴퓨터", "프로그램 카운터", } },
{ "CD", new List<string> { "컴팩트 디스크", "캐시 디스펜서", } },
};
// 딕셔너리에 추가한다
var key = "EC";
var value = "전자상거래";
if (dict.ContainsKey(key)) {
dict[key].Add(value);
} else {
dict[key] = new List<string> { value };
}
// 딕셔너리의 내용을 열거한다
foreach (var item in dict) {
foreach (var term in item.Value) {
Console.WriteLine("{0} : {1}", item.Key, term);
}
}
/*
[결과]
PC : 퍼스널 컴퓨터
PC : 프로그램 카운터
CD : 컴팩트 디스크
CD : 캐시 디스펜서
EC : 전자상거래
*/
연습 문제
문제 7.1
static void Main(string[] args)
{
// 1
var str = "Cozy lummox gives smart squid who asks for job pen";
var str2 = str.Replace(" ", "").ToUpper();
var dic = new Dictionary<char, int>();
foreach (var item in str2)
{
if (dic.ContainsKey(item))
{
dic[item] = ++dic[item];
}
else
{
dic[item] = 1;
}
}
foreach (var item in dic.OrderBy(s => s.Key))
{
Console.WriteLine($"'{item.Key}' : {item.Value}");
}
// 2
var sortedDic = new SortedDictionary<char, int>(dic);
}
리뷰
1. dic[item] = ++dic[item]; 을 dic[item]++; 로 바꾸면 되는데 너무 복잡하게 작성했다.
예제 코드에서 if ('A' <= uc && uc <= 'Z') {} 구문은 꼭 필요했는지 의문이다.
또, var uc = char.ToUpper(c); 코드도 굳이 반복문 안에 들어가야했는지 의문이다.
내 코드가 더 나은 듯?
2. 난 SortedDictionary 클래스 안에 내장되어 있는 Convert 기능 사용했는데 예제는 처음부터 다시 작성했네
차이점 보면 Dictionary는 OrderBy 해줘야하지만 SortedDictionary는 자동 정렬되서 출력된다.
그래서 SortedDictionary 군!
검색해서 나온 Dictionary랑 SortedDictionary 차이점
A SortedDictionary is implemented as a binary search tree. Therefore, accessing an element is O(lg(n)). A Dictionary is a hash table, and has a complexity of O(1) for access.
A SortedDictionary is quite useful when you need the data to be sorted (a Dictionary has no defined order). Dictionary is appropriate for most cases.
예제 코드
class Program {
static void Main(string[] args) {
var text = "Cozy lummox gives smart squid who asks for job pen";
Exercise1_1(text);
Console.WriteLine();
Exercise1_2(text);
}
static void Exercise1_1(string text) {
var dict = new Dictionary<Char, int>();
foreach (var c in text) {
var uc = char.ToUpper(c);
if ('A' <= uc && uc <= 'Z') {
if (dict.ContainsKey(uc))
dict[uc]++;
else
dict[uc] = 1;
}
}
foreach (var item in dict.OrderBy(x => x.Key))
Console.WriteLine("{0}:{1}", item.Key, item.Value);
}
static void Exercise1_2(string text) {
var dict = new SortedDictionary<Char, int>();
foreach (var c in text) {
var uc = char.ToUpper(c);
if ('A' <= uc && uc <= 'Z') {
if (dict.ContainsKey(uc))
dict[uc]++;
else
dict[uc] = 1;
}
}
foreach (var item in dict)
Console.WriteLine("{0}:{1}", item.Key, item.Value);
}
}
문제 7.2
class Program
{
...
// 3번
var canRemove = abbrs.Remove("IOC");
Console.WriteLine(canRemove);
var count = abbrs.Count;
Console.WriteLine(count);
// 4번
abbrs.Find3Char();
...
}
class Abbreviations
{
...
// 1번
public int Count => _dict.Count;
// 2번
public bool Remove(string abbr)
{
if (_dict.ContainsKey(abbr))
return true;
return false;
}
// 4번
public void Find3Char()
{
var newDict = _dict.Where(w => w.Key.Count() == 3);
foreach (var item in newDict)
{
Console.WriteLine($"{item.Key}={item.Value}");
}
}
...
}
리뷰
2. Remove 메서드는 요소를 성공적으로 찾아서 제거한 경우 true이고, 그렇지 않으면 false 반환한다. 괜히 if문으로 이중체크 했네!
4. 길이 구하는 걸 때 습관적으로 Count 메서드 쓴다. 3글자로 된 줄임말을 출력하는거니 글자 길이를 확실하게 나타내는 Length 메서드를 사용하는게 좋을 듯 하다.
What 보다는 How를 생각하자!
예제 코드
class Program
{
...
// 7.2.3 (Count를 호출한 예)
// 위에 나온 Add 메서드에서 두 개의 오브젝트를 추가했으므로 읽어들인 단어 수+2가 Count의 값이 된다
var count = abbrs.Count;
Console.WriteLine(abbrs.Count);
Console.WriteLine();
// 7.2.3 (Remove를 호출한 예)
if (abbrs.Remove("NPT"))
Console.WriteLine(abbrs.Count);
if (!abbrs.Remove("NPT"))
Console.WriteLine("삭제할 수 없습니다.");
Console.WriteLine();
// 7.2.4
// IEnumerable<>를 구현했으므로 LINQ를 사용할 수 있다
foreach (var item in abbrs.Where(x => x.Key.Length == 3))
Console.WriteLine("{0}={1}", item.Key, item.Value);
...
}
class Abbreviations
{
...
// 7.2.1
public int Count {
get {
return _dict.Count;
}
}
// 7.2.2
public bool Remove(string abb) {
return _dict.Remove(abb);
}
...
}