수색…
사전 열거
다음 세 가지 방법 중 하나로 사전을 열거 할 수 있습니다.
KeyValue 쌍 사용
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(KeyValuePair<int, string> kvp in dict)
{
Console.WriteLine("Key : " + kvp.Key.ToString() + ", Value : " + kvp.Value);
}
키 사용하기
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(int key in dict.Keys)
{
Console.WriteLine("Key : " + key.ToString() + ", Value : " + dict[key]);
}
값 사용
Dictionary<int, string> dict = new Dictionary<int, string>();
foreach(string s in dict.Values)
{
Console.WriteLine("Value : " + s);
}
컬렉션 초기화 프로그램으로 사전 초기화하기
// Translates to `dict.Add(1, "First")` etc.
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
// Translates to `dict[1] = "First"` etc.
// Works in C# 6.0.
var dict = new Dictionary<int, string>()
{
[1] = "First",
[2] = "Second",
[3] = "Third"
};
사전에 추가하기
Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1, "First");
dict.Add(2, "Second");
// To safely add items (check to ensure item does not already exist - would throw)
if(!dict.ContainsKey(3))
{
dict.Add(3, "Third");
}
또는 인덱서를 통해 추가 / 설정 될 수 있습니다. 인덱서는 내부적으로 get 및 set을 갖는 속성처럼 보이지만 대괄호로 지정된 모든 유형의 매개 변수를 사용합니다.
Dictionary<int, string> dict = new Dictionary<int, string>();
dict[1] = "First";
dict[2] = "Second";
dict[3] = "Third";
예외를 throw하는 Add
메서드와 달리 사전에 키가 포함되어 있으면 인덱서가 기존 값을 바꿉니다.
스레드로부터 안전한 사전 사용을 위해 ConcurrentDictionary<TKey, TValue>
:
var dict = new ConcurrentDictionary<int, string>();
dict.AddOrUpdate(1, "First", (oldKey, oldValue) => "First");
사전에서 가치 얻기
주어진 설치 코드 :
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
키가 1 인 항목의 값을 읽고 싶을 수 있습니다. 키가 존재하지 않으면 값을 가져 오면 KeyNotFoundException
이 발생하므로 ContainsKey
를 사용하여 먼저 확인해야 할 수 ContainsKey
.
if (dict.ContainsKey(1))
Console.WriteLine(dict[1]);
이것은 한 가지 단점이 있습니다. 사전을 두 번 검색합니다 (한 번은 존재를 확인하고 다른 하나는 값을 읽음). 큰 사전의 경우 성능에 영향을 줄 수 있습니다. 다행히 두 작업을 함께 수행 할 수 있습니다.
string value;
if (dict.TryGetValue(1, out value))
Console.WriteLine(value);
사전 만들기 Case-Insensivitve 키를 사용하십시오.
var MyDict = new Dictionary<string,T>(StringComparison.InvariantCultureIgnoreCase)
동시 사전 (.NET 4.0부터)
여러 스레드에서 동시에 액세스 할 수있는 키 / 값 쌍의 스레드 안전 컬렉션을 나타냅니다.
인스턴스 만들기
인스턴스를 생성하는 것은 Dictionary<TKey, TValue>
와 거의 같은 방식으로 작동합니다. 예 :
var dict = new ConcurrentDictionary<int, string>();
추가 또는 업데이트 중
Add
메서드가 없다는 것에 놀랄 것입니다. 대신에 두 개의 오버로드가있는 AddOrUpdate
가 있습니다.
AddOrUpdate(TKey key, TValue, Func<TKey, TValue, TValue> addValue)
- 키가 존재하지 않으면 키 / 값 쌍을 추가하거나 키가 존재하면 지정된 함수를 사용하여 키 / 값 쌍을 업데이트합니다. 이미 존재 함.
(2) AddOrUpdate(TKey key, Func<TKey, TValue> addValue, Func<TKey, TValue, TValue> updateValueFactory)
- 지정된 함수를 사용하여 키가 없거나 키가없는 경우 키 / 값 쌍을 추가합니다. 키가 이미 존재하면 키 / 값 쌍을 업데이트하십시오.
지정된 key (1)에 대해 값이 이미 존재하는 경우에도 값을 추가하거나 업데이트합니다.
string addedValue = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => "First");
이전 값 (1)을 기준으로 값을 추가 또는 업데이트했지만 업데이트의 값을 변경합니다.
string addedValue2 = dict.AddOrUpdate(1, "First", (updateKey, valueOld) => $"{valueOld} Updated");
overload (2)를 사용하여 팩토리를 사용하여 새로운 값을 추가 할 수도 있습니다.
string addedValue3 = dict.AddOrUpdate(1, (key) => key == 1 ? "First" : "Not First", (updateKey, valueOld) => $"{valueOld} Updated");
가치 창출
값을 얻는 것은 Dictionary<TKey,TValue>
와 동일합니다.
string value = null;
bool success = dict.TryGetValue(1, out value);
값 가져 오기 또는 추가하기
스레드 오버런 (thread-safe) 방식으로 값을 얻거나 추가 하는 두 가지의 과부하가 있습니다.
키 2가있는 값을 가져 오거나, 키가없는 경우 값 "두 번째"를 추가하십시오.
string theValue = dict.GetOrAdd(2, "Second");
값이없는 경우 값을 추가하는 팩토리 사용 :
string theValue2 = dict.GetOrAdd(2, (key) => key == 2 ? "Second" : "Not Second." );
IEnumerable 사전 (≥ .NET 3.5)
IEnumerable <T> 에서 사전 <TKey, TValue> 만들기 :
using System;
using System.Collections.Generic;
using System.Linq;
public class Fruits
{
public int Id { get; set; }
public string Name { get; set; }
}
var fruits = new[]
{
new Fruits { Id = 8 , Name = "Apple" },
new Fruits { Id = 3 , Name = "Banana" },
new Fruits { Id = 7 , Name = "Mango" },
};
// Dictionary<int, string> key value
var dictionary = fruits.ToDictionary(x => x.Id, x => x.Name);
사전에서 제거
주어진 설치 코드 :
var dict = new Dictionary<int, string>()
{
{ 1, "First" },
{ 2, "Second" },
{ 3, "Third" }
};
Remove
메서드를 사용하여 키와 관련 값을 제거합니다.
bool wasRemoved = dict.Remove(2);
이 코드를 실행하면 키 2
와 그 값이 사전에서 제거됩니다. Remove
는 지정된 키가 발견되어 사전에서 제거되었는지 여부를 나타내는 부울 값을 반환합니다. 키가 사전에 없으면 사전에서 아무 것도 제거되지 않고 false가 반환됩니다 (예외가 발생하지 않음).
키의 값을 null
로 설정하여 키를 제거하려고하면 올바르지 않습니다 .
dict[2] = null; // WRONG WAY TO REMOVE!
키를 제거하지 않습니다. 이전 값을 null
값으로 null
.
사전에서 모든 키와 값을 제거하려면 Clear
메소드를 사용하십시오.
dict.Clear();
Clear
실행하면 사전의 Count
는 0이되지만 내부 용량은 변경되지 않습니다.
ContainsKey (TKey)
Dictionary
에 특정 키가 있는지 확인하려면 ContainsKey(TKey)
메서드를 호출하고 TKey
유형의 키를 제공 할 수 있습니다. 이 메서드는 키가 사전에있을 때 bool
값을 반환합니다. 샘플 :
var dictionary = new Dictionary<string, Customer>()
{
{"F1", new Customer() { FirstName = "Felipe", ... } },
{"C2", new Customer() { FirstName = "Carl", ... } },
{"J7", new Customer() { FirstName = "John", ... } },
{"M5", new Customer() { FirstName = "Mary", ... } },
};
사전에 C2
가 있는지 확인하십시오.
if (dictionary.ContainsKey("C2"))
{
// exists
}
ContainsKey 메서드는 제네릭 버전 Dictionary<TKey, TValue>
에서 사용할 수 있습니다.
사전 목록
KeyValuePair 목록 만들기 :
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<KeyValuePair<int, int>> list = new List<KeyValuePair<int, int>>();
list.AddRange(dictionary);
키 목록 만들기 :
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Keys);
값 목록 만들기 :
Dictionary<int, int> dictionary = new Dictionary<int, int>();
List<int> list = new List<int>();
list.AddRange(dictionary.Values);
ConcurrentDictionary가 Lazy'1로 확장되면 중복 계산이 줄어 듭니다.
문제
ConcurrentDictionary는 캐시에서 기존 키를 즉시 반환하고 대부분 잠금을 해제하고 세분화 된 수준에서 경합하는 경우에 빛을 발합니다. 그러나 객체 생성이 실제로 비싸고 컨텍스트 전환 비용을 초과하고 캐시 미스가 발생하면 어떻게 될까요?
동일한 키가 여러 스레드에서 요청 된 경우 충돌 작업으로 인해 생성 된 개체 중 하나가 컬렉션에 추가되고 다른 개체는 삭제되어 개체를 만드는 CPU 리소스와 개체를 임시로 저장하는 메모리 리소스가 낭비됩니다 . 다른 리소스도 낭비 될 수 있습니다. 이것은 정말로 나쁘다.
해결책
ConcurrentDictionary<TKey, TValue>
와 Lazy<TValue>
조합 할 수 있습니다. ConcurrentDictionary GetOrAdd 메서드는 실제로 컬렉션에 추가 된 값만 반환 할 수 있습니다. Lazy 객체가 느슨해 진 것은이 경우에도 낭비 될 수 있지만 Lazy 객체 자체는 상대적으로 비싸지 않으므로 별 문제는 아닙니다. 잃어버린 Lazy의 Value 속성은 GetOrAdd 메서드에서 반환 된 컬렉션의 Value 속성을 실제로 요청하기 때문에 결코 요청되지 않습니다.
public static class ConcurrentDictionaryExtensions
{
public static TValue GetOrCreateLazy<TKey, TValue>(
this ConcurrentDictionary<TKey, Lazy<TValue>> d,
TKey key,
Func<TKey, TValue> factory)
{
return
d.GetOrAdd(
key,
key1 =>
new Lazy<TValue>(() => factory(key1),
LazyThreadSafetyMode.ExecutionAndPublication)).Value;
}
}
XmlSerializer 개체의 캐싱은 특히 비쌀 수 있으며 응용 프로그램 시작시 많은 경합이 있습니다. 그리고 더 많은 것들이 있습니다 : 그것들이 커스텀 시리얼 라이저라면, 나머지 프로세스 수명주기 동안 메모리 누출이있을 것입니다. 이 경우 ConcurrentDictionary의 유일한 이점은 나머지 프로세스 라이프 사이클 동안 잠금이 없지만 응용 프로그램 시작 및 메모리 사용은 용인 될 수 없다는 것입니다. 이것은 Lazy와 함께 ConcurrentDictionary를위한 작업입니다.
private ConcurrentDictionary<Type, Lazy<XmlSerializer>> _serializers =
new ConcurrentDictionary<Type, Lazy<XmlSerializer>>();
public XmlSerializer GetSerialier(Type t)
{
return _serializers.GetOrCreateLazy(t, BuildSerializer);
}
private XmlSerializer BuildSerializer(Type t)
{
throw new NotImplementedException("and this is a homework");
}