수색…


사전 열거

다음 세 가지 방법 중 하나로 사전을 열거 할 수 있습니다.

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");
}


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow