.NET 사전에 중복된 키가 있습니까?
-
02-07-2019 - |
문제
.NET 기본 클래스 라이브러리에 중복 키를 사용할 수 있는 사전 클래스가 있습니까?내가 찾은 유일한 해결책은 예를 들어 다음과 같은 클래스를 만드는 것입니다.
Dictionary<string, List<object>>
하지만 실제로 사용하기에는 상당히 짜증나는 부분입니다.Java에서는 MultiMap이 이를 수행한다고 생각하지만 .NET에서는 아날로그를 찾을 수 없습니다.
해결책
.NET 3.5를 사용하는 경우 사용하십시오 Lookup
수업.
편집 : 일반적으로 a Lookup
사용 Enumerable.ToLookup
. 이것은 당신이 나중에 그것을 바꿀 필요가 없다고 가정하지만, 나는 일반적으로 그것이 충분하다고 생각합니다.
만약 그렇다면 그렇지 않습니다 당신을 위해 일하고, 나는 틀에 도움이 될 프레임 워크에 어떤 것이 있다고 생각하지 않습니다. 그리고 사전을 사용하는 것은 그것이 얻는 것만 큼 좋습니다 :(
다른 팁
목록 클래스는 실제로 컬렉션을 반복하려는 중복을 포함하는 키/값 컬렉션에 매우 효과적입니다. 예시:
List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
// add some values to the collection here
for (int i = 0; i < list.Count; i++)
{
Print(list[i].Key, list[i].Value);
}
다음은 List <keyValuePair <string, String >> 로이 작업을 수행하는 한 가지 방법입니다.
public class ListWithDuplicates : List<KeyValuePair<string, string>>
{
public void Add(string key, string value)
{
var element = new KeyValuePair<string, string>(key, value);
this.Add(element);
}
}
var list = new ListWithDuplicates();
list.Add("k1", "v1");
list.Add("k1", "v2");
list.Add("k1", "v3");
foreach(var item in list)
{
string x = string.format("{0}={1}, ", item.Key, item.Value);
}
출력 k1 = v1, k1 = v2, k1 = v3
문자열을 키와 값 모두로 사용하는 경우 다음을 사용할 수 있습니다. System.Collections.Specialized.NameValueCollection, GetValues(string key) 메서드를 통해 문자열 값의 배열을 반환합니다.
나는 방금 만났다 PowerCollections 무엇보다도 Multidictionary라는 클래스를 포함하는 라이브러리. 이것은 이러한 유형의 기능을 깔끔하게 래핑합니다.
Lookup 사용에 관한 매우 중요한 참고 사항:
다음의 인스턴스를 생성할 수 있습니다. Lookup(TKey, TElement)
전화로 ToLookup
구현하는 객체에 IEnumerable(T)
새 인스턴스를 생성하는 공개 생성자가 없습니다. Lookup(TKey, TElement)
.추가적으로, Lookup(TKey, TElement)
객체는 불변입니다. 즉, 객체에서 요소나 키를 추가하거나 제거할 수 없습니다. Lookup(TKey, TElement)
객체가 생성된 후입니다.
나는 이것이 대부분의 용도에 대한 쇼 스토퍼가 될 것이라고 생각합니다.
나는 같은 것을 생각한다 List<KeyValuePair<object, object>>
일을 할 것입니다.
> = .NET 4를 사용하는 경우 사용할 수 있습니다. Tuple
수업:
// declaration
var list = new List<Tuple<string, List<object>>>();
// to add an item to the list
var item = Tuple<string, List<object>>("key", new List<object>);
list.Add(item);
// to iterate
foreach(var i in list)
{
Console.WriteLine(i.Item1.ToString());
}
"중복 키"항목을 허용하는 사전의 "나만의 롤"버전을 쉽게 만듭니다. 다음은 거친 간단한 구현입니다. 기본적으로 대부분 (전부는 아니지만)에 대한 지원을 추가하는 것을 고려할 수 있습니다. IDictionary<T>
.
public class MultiMap<TKey,TValue>
{
private readonly Dictionary<TKey,IList<TValue>> storage;
public MultiMap()
{
storage = new Dictionary<TKey,IList<TValue>>();
}
public void Add(TKey key, TValue value)
{
if (!storage.ContainsKey(key)) storage.Add(key, new List<TValue>());
storage[key].Add(value);
}
public IEnumerable<TKey> Keys
{
get { return storage.Keys; }
}
public bool ContainsKey(TKey key)
{
return storage.ContainsKey(key);
}
public IList<TValue> this[TKey key]
{
get
{
if (!storage.ContainsKey(key))
throw new KeyNotFoundException(
string.Format(
"The given key {0} was not found in the collection.", key));
return storage[key];
}
}
}
사용 방법에 대한 빠른 예 :
const string key = "supported_encodings";
var map = new MultiMap<string,Encoding>();
map.Add(key, Encoding.ASCII);
map.Add(key, Encoding.UTF8);
map.Add(key, Encoding.Unicode);
foreach (var existingKey in map.Keys)
{
var values = map[existingKey];
Console.WriteLine(string.Join(",", values));
}
원래 질문에 대한 답으로. 같은 것 Dictionary<string, List<object>>
호출 된 클래스에서 구현됩니다 MultiMap
에서 Code Project
.
아래 링크에 대한 추가 정보를 찾을 수 있습니다.http://www.codeproject.com/kb/cs/multikeydictionary.aspx
NameValueCollection은 하나의 키 (문자열이기도 함)에서 여러 문자열 값을 지원하지만 내가 알고있는 유일한 예입니다.
나는 그런 종류의 기능이 필요한 상황에 빠질 때 당신의 예제와 유사한 구성을 만드는 경향이 있습니다.
사용할 때 List<KeyValuePair<string, object>>
옵션, LINQ를 사용하여 검색을 수행 할 수 있습니다.
List<KeyValuePair<string, object>> myList = new List<KeyValuePair<string, object>>();
//fill it here
var q = from a in myList Where a.Key.Equals("somevalue") Select a.Value
if(q.Count() > 0){ //you've got your value }
내가 사용하는 방식은 단지 A입니다
Dictionary<string, List<string>>
이렇게하면 문자열 목록을 보유하는 단일 키가 있습니다.
예시:
List<string> value = new List<string>();
if (dictionary.Contains(key)) {
value = dictionary[key];
}
value.Add(newValue);
실제 복제본이 아니라 합동을 의미합니까? 그렇지 않으면 해시 가능이 작동하지 않습니다.
합동은 두 개의 개별 키가 동등한 값으로 해시 될 수 있지만 키는 같지 않다는 것을 의미합니다.
예를 들어 : 해시 가능의 해시 함수가 단지 hashval = key mod 3이라고 가정 해보십시오. 1과 4는 모두 1에 맵이지만 다른 값이라고 가정하십시오. 이것은 목록에 대한 당신의 아이디어가 나오는 곳입니다.
1을 조회 해야하는 경우 해당 값은 1으로 해시됩니다. 키 = 1이 발견 될 때까지 목록이 횡단됩니다.
중복 키를 삽입 할 수있게되면 어떤 키 맵을 어떤 값으로 구별 할 수 없습니다.
동일한 답변을 찾아이 게시물을 우연히 발견하고 아무것도 찾지 못했기 때문에 사전 목록을 사용하여 베어 본 예제 솔루션을 조작하여 [] 연산자를 우선하여 다른 모든 사람들이있는 경우 목록에 새 사전을 추가했습니다. 주어진 키 (set), 값 목록 (get)을 반환합니다.
추악하고 비효율적이며 Key로만/세트 만 가져오고 항상 목록을 반환하지만 작동합니다.
class DKD {
List<Dictionary<string, string>> dictionaries;
public DKD(){
dictionaries = new List<Dictionary<string, string>>();}
public object this[string key]{
get{
string temp;
List<string> valueList = new List<string>();
for (int i = 0; i < dictionaries.Count; i++){
dictionaries[i].TryGetValue(key, out temp);
if (temp == key){
valueList.Add(temp);}}
return valueList;}
set{
for (int i = 0; i < dictionaries.Count; i++){
if (dictionaries[i].ContainsKey(key)){
continue;}
else{
dictionaries[i].Add(key,(string) value);
return;}}
dictionaries.Add(new Dictionary<string, string>());
dictionaries.Last()[key] =(string)value;
}
}
}
@Hector Correa의 답변을 일반 유형의 확장으로 변경하고 사용자 지정 trygetValue도 추가했습니다.
public static class ListWithDuplicateExtensions
{
public static void Add<TKey, TValue>(this List<KeyValuePair<TKey, TValue>> collection, TKey key, TValue value)
{
var element = new KeyValuePair<TKey, TValue>(key, value);
collection.Add(element);
}
public static int TryGetValue<TKey, TValue>(this List<KeyValuePair<TKey, TValue>> collection, TKey key, out IEnumerable<TValue> values)
{
values = collection.Where(pair => pair.Key.Equals(key)).Select(pair => pair.Value);
return values.Count();
}
}
이것은 견인 방법 동시 사전입니다. 이것이 당신에게 도움이 될 것이라고 생각합니다.
public class HashMapDictionary<T1, T2> : System.Collections.IEnumerable
{
private System.Collections.Concurrent.ConcurrentDictionary<T1, List<T2>> _keyValue = new System.Collections.Concurrent.ConcurrentDictionary<T1, List<T2>>();
private System.Collections.Concurrent.ConcurrentDictionary<T2, List<T1>> _valueKey = new System.Collections.Concurrent.ConcurrentDictionary<T2, List<T1>>();
public ICollection<T1> Keys
{
get
{
return _keyValue.Keys;
}
}
public ICollection<T2> Values
{
get
{
return _valueKey.Keys;
}
}
public int Count
{
get
{
return _keyValue.Count;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
public List<T2> this[T1 index]
{
get { return _keyValue[index]; }
set { _keyValue[index] = value; }
}
public List<T1> this[T2 index]
{
get { return _valueKey[index]; }
set { _valueKey[index] = value; }
}
public void Add(T1 key, T2 value)
{
lock (this)
{
if (!_keyValue.TryGetValue(key, out List<T2> result))
_keyValue.TryAdd(key, new List<T2>() { value });
else if (!result.Contains(value))
result.Add(value);
if (!_valueKey.TryGetValue(value, out List<T1> result2))
_valueKey.TryAdd(value, new List<T1>() { key });
else if (!result2.Contains(key))
result2.Add(key);
}
}
public bool TryGetValues(T1 key, out List<T2> value)
{
return _keyValue.TryGetValue(key, out value);
}
public bool TryGetKeys(T2 value, out List<T1> key)
{
return _valueKey.TryGetValue(value, out key);
}
public bool ContainsKey(T1 key)
{
return _keyValue.ContainsKey(key);
}
public bool ContainsValue(T2 value)
{
return _valueKey.ContainsKey(value);
}
public void Remove(T1 key)
{
lock (this)
{
if (_keyValue.TryRemove(key, out List<T2> values))
{
foreach (var item in values)
{
var remove2 = _valueKey.TryRemove(item, out List<T1> keys);
}
}
}
}
public void Remove(T2 value)
{
lock (this)
{
if (_valueKey.TryRemove(value, out List<T1> keys))
{
foreach (var item in keys)
{
var remove2 = _keyValue.TryRemove(item, out List<T2> values);
}
}
}
}
public void Clear()
{
_keyValue.Clear();
_valueKey.Clear();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _keyValue.GetEnumerator();
}
}
예 :
public class TestA
{
public int MyProperty { get; set; }
}
public class TestB
{
public int MyProperty { get; set; }
}
HashMapDictionary<TestA, TestB> hashMapDictionary = new HashMapDictionary<TestA, TestB>();
var a = new TestA() { MyProperty = 9999 };
var b = new TestB() { MyProperty = 60 };
var b2 = new TestB() { MyProperty = 5 };
hashMapDictionary.Add(a, b);
hashMapDictionary.Add(a, b2);
hashMapDictionary.TryGetValues(a, out List<TestB> result);
foreach (var item in result)
{
//do something
}
나는이 간단한 수업을 사용합니다.
public class ListMap<T,V> : List<KeyValuePair<T, V>>
{
public void Add(T key, V value) {
Add(new KeyValuePair<T, V>(key, value));
}
public List<V> Get(T key) {
return FindAll(p => p.Key.Equals(key)).ConvertAll(p=> p.Value);
}
}
용법:
var fruits = new ListMap<int, string>();
fruits.Add(1, "apple");
fruits.Add(1, "orange");
var c = fruits.Get(1).Count; //c = 2;
u는 사전을 사용하려는 모든 곳에서 복합 문자열 키를 구축하는 방법을 정의 할 수 있습니다.
private string keyBuilder(int key1, int key2)
{
return string.Format("{0}/{1}", key1, key2);
}
사용을 위해 :
myDict.ContainsKey(keyBuilder(key1, key2))
중복 키는 사전의 전체 계약을 중단합니다. 사전에서 각 키는 독특하고 단일 값으로 매핑됩니다. 객체를 임의의 추가 객체에 연결하려면 가장 좋은 방법은 데이터 세트와 유사한 것일 수 있습니다 (공통의 관점에서는 테이블). 키를 한 열에 넣고 다른 열에 넣으십시오. 이것은 사전보다 현저히 느리지 만 핵심 객체를 해시 할 수있는 능력을 잃어버린 트레이드 오프입니다.
또한 이것은 가능합니다.
Dictionary<string, string[]> previousAnswers = null;
이런 식으로, 우리는 고유 한 키를 가질 수 있습니다. 이것이 당신을 위해 효과가 있기를 바랍니다.
다른 케이스와 같은 동일한 키를 추가 할 수 있습니다.
Key1
Key1
Key1
Key1
Key1
Key1
나는 더미 대답이라는 것을 알고 있지만 나를 위해 일했다.