C #에서 튜플 (또는 배열)을 사전 키로 사용
C #에서 사전 조회 테이블을 만들려고합니다. 3 튜플의 값을 하나의 문자열로 해결해야합니다. 배열을 키로 사용해 보았지만 작동하지 않았고 다른 작업을해야할지 모르겠습니다. 이 시점에서 나는 사전 사전을 만드는 것을 고려하고 있지만, 자바 스크립트로하는 방법이지만보기에는보기에 그리 예쁘지 않을 것입니다.
.NET 4.0을 사용하는 경우 Tuple을 사용하십시오.
lookup = new Dictionary<Tuple<TypeA, TypeB, TypeC>, string>();
그렇지 않은 경우 튜플을 정의하고 키로 사용할 수 있습니다. Tuple은 GetHashCode, Equals 및 IEquatable을 재정의해야합니다.
struct Tuple<T, U, W> : IEquatable<Tuple<T,U,W>>
{
readonly T first;
readonly U second;
readonly W third;
public Tuple(T first, U second, W third)
{
this.first = first;
this.second = second;
this.third = third;
}
public T First { get { return first; } }
public U Second { get { return second; } }
public W Third { get { return third; } }
public override int GetHashCode()
{
return first.GetHashCode() ^ second.GetHashCode() ^ third.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
return Equals((Tuple<T, U, W>)obj);
}
public bool Equals(Tuple<T, U, W> other)
{
return other.first.Equals(first) && other.second.Equals(second) && other.third.Equals(third);
}
}
튜플과 중첩 된 사전 기반 접근 방식 사이에서는 거의 항상 튜플 기반을 사용하는 것이 좋습니다.
유지 관리의 관점에서 ,
다음과 같은 기능을 구현하는 것이 훨씬 쉽습니다.
var myDict = new Dictionary<Tuple<TypeA, TypeB, TypeC>, string>();
...보다
var myDict = new Dictionary<TypeA, Dictionary<TypeB, Dictionary<TypeC, string>>>();
수신자 측에서. 두 번째 경우 각 추가, 조회, 제거 등은 둘 이상의 사전에 대한 조치가 필요합니다.
또한 복합 키에 향후 하나 이상의 필드가 필요한 경우 중첩 된 사전과 후속 검사를 추가해야하므로 두 번째 경우 (중첩 된 사전)에서 코드를 상당히 많이 변경해야합니다.
성능 관점 에서 도달 할 수있는 최상의 결론은 직접 측정하는 것입니다. 그러나 미리 고려할 수있는 몇 가지 이론적 한계가 있습니다.
중첩 된 딕셔너리의 경우 모든 키 (외부 및 내부)에 대한 추가 딕셔너리가 있으면 약간의 메모리 오버 헤드가 발생합니다 (튜플을 만드는 것보다 더 많음).
중첩 된 사전의 경우 추가, 업데이트, 조회, 제거 등과 같은 모든 기본 작업을 두 개의 사전에서 수행해야합니다. 이제 중첩 된 사전 접근 방식이 더 빠를 수있는 경우가 있습니다. 즉, 조회되는 데이터가 없을 때 중간 사전이 전체 해시 코드 계산 및 비교를 우회 할 수 있지만 다시 확인해야합니다. 데이터가있는 경우 조회를 두 번 (또는 중첩에 따라 세 번) 수행해야하므로 속도가 느려집니다.
튜플 접근 방식과 관련하여 .NET 튜플은 세트의 키로 사용되어야 할 때 가장 성능이 좋지 않습니다. 그
Equals
및GetHashCode
구현으로 인해 값 유형에 대한 박싱이 발생하기 때문 입니다.
튜플 기반 사전을 사용하지만 더 많은 성능을 원한다면 더 나은 구현으로 내 튜플을 사용합니다.
참고로 사전을 멋지게 만들 수있는 화장품은 거의 없습니다.
인덱서 스타일 호출은 훨씬 깔끔하고 직관적 일 수 있습니다. 예를 들어,
string foo = dict[a, b, c]; //lookup dict[a, b, c] = ""; //update/insertion
따라서 삽입과 조회를 내부적으로 처리하는 사전 클래스에 필요한 인덱서를 노출하십시오.
또한 적절한
IEnumerable
인터페이스를 구현하고Add(TypeA, TypeB, TypeC, string)
다음과 같이 컬렉션 이니셜 라이저 구문을 제공하는 메서드를 제공합니다.new MultiKeyDictionary<TypeA, TypeB, TypeC, string> { { a, b, c, null }, ... };
좋고, 깨끗하고, 빠르고, 쉽고, 읽기 쉬운 방법은 다음과 같습니다.
- 현재 유형에 대해 동등 멤버 (Equals () 및 GetHashCode ()) 를 생성합니다. ReSharper 와 같은 도구 는 메서드를 생성 할뿐만 아니라 동등성 검사 및 / 또는 해시 코드 계산에 필요한 코드도 생성합니다. 생성 된 코드는 Tuple 실현보다 최적입니다.
- 튜플에서 파생 된 간단한 키 클래스를 만드십시오 .
다음과 비슷한 것을 추가하십시오.
public sealed class myKey : Tuple<TypeA, TypeB, TypeC>
{
public myKey(TypeA dataA, TypeB dataB, TypeC dataC) : base (dataA, dataB, dataC) { }
public TypeA DataA => Item1;
public TypeB DataB => Item2;
public TypeC DataC => Item3;
}
따라서 사전과 함께 사용할 수 있습니다.
var myDictinaryData = new Dictionary<myKey, string>()
{
{new myKey(1, 2, 3), "data123"},
{new myKey(4, 5, 6), "data456"},
{new myKey(7, 8, 9), "data789"}
};
- 계약서에서도 사용할 수 있습니다.
- linq에서 조인 또는 그룹화를위한 키로
- 이렇게하면 Item1, Item2, Item3의 순서를 잘못 입력하지 않습니다.
- 무언가를 얻기 위해 어디로 가야하는지 이해하기 위해 코드를 기억하거나 살펴볼 필요가 없습니다.
- IStructuralEquatable, IStructuralComparable, IComparable, ITuple을 모두 재정의 할 필요가 없습니다.
어떤 이유로 든 자신 만의 Tuple 클래스를 만들거나 .NET 4.0에 내장 된 클래스를 사용하지 않으려는 경우 가능한 다른 방법이 하나 있습니다. 세 개의 키 값을 하나의 값으로 결합 할 수 있습니다.
예를 들어, 세 값이 64 비트를 넘지 않는 정수 유형 인 경우이를 결합하여 ulong
.
Worst-case you can always use a string, as long as you make sure the three components in it are delimited with some character or sequence that does not occur inside the components of the key, for example, with three numbers you could try:
string.Format("{0}#{1}#{2}", key1, key2, key3)
There is obviously some composition overhead in this approach, but depending on what you are using it for this may be trivial enough not to care about it.
If you're on C# 7, you should consider using value tuples as your composite key. Value tuples typically offer better performance than the traditional reference tuples (Tuple<T1, …>
) since value tuples are value types (structs), not reference types, so they avoid the memory allocation and garbage collection costs. Also, they offer conciser and more intuitive syntax, allowing for their fields to be named if you so wish. They also implement the IEquatable<T>
interface needed for the dictionary.
var dict = new Dictionary<(int PersonId, int LocationId, int SubjectId), string>();
dict.Add((3, 6, 9), "ABC");
dict.Add((PersonId: 4, LocationId: 9, SubjectId: 10), "XYZ");
var personIds = dict.Keys.Select(k => k.PersonId).Distinct().ToList();
I would override your Tuple with a proper GetHashCode, and just use it as the key.
As long as you overload the proper methods, you should see decent performance.
Here is the .NET tuple for reference:
[Serializable]
public class Tuple<T1, T2, T3> : IStructuralEquatable, IStructuralComparable, IComparable, ITuple {
private readonly T1 m_Item1;
private readonly T2 m_Item2;
private readonly T3 m_Item3;
public T1 Item1 { get { return m_Item1; } }
public T2 Item2 { get { return m_Item2; } }
public T3 Item3 { get { return m_Item3; } }
public Tuple(T1 item1, T2 item2, T3 item3) {
m_Item1 = item1;
m_Item2 = item2;
m_Item3 = item3;
}
public override Boolean Equals(Object obj) {
return ((IStructuralEquatable) this).Equals(obj, EqualityComparer<Object>.Default);;
}
Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) {
if (other == null) return false;
Tuple<T1, T2, T3> objTuple = other as Tuple<T1, T2, T3>;
if (objTuple == null) {
return false;
}
return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3);
}
Int32 IComparable.CompareTo(Object obj) {
return ((IStructuralComparable) this).CompareTo(obj, Comparer<Object>.Default);
}
Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) {
if (other == null) return 1;
Tuple<T1, T2, T3> objTuple = other as Tuple<T1, T2, T3>;
if (objTuple == null) {
throw new ArgumentException(Environment.GetResourceString("ArgumentException_TupleIncorrectType", this.GetType().ToString()), "other");
}
int c = 0;
c = comparer.Compare(m_Item1, objTuple.m_Item1);
if (c != 0) return c;
c = comparer.Compare(m_Item2, objTuple.m_Item2);
if (c != 0) return c;
return comparer.Compare(m_Item3, objTuple.m_Item3);
}
public override int GetHashCode() {
return ((IStructuralEquatable) this).GetHashCode(EqualityComparer<Object>.Default);
}
Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) {
return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3));
}
Int32 ITuple.GetHashCode(IEqualityComparer comparer) {
return ((IStructuralEquatable) this).GetHashCode(comparer);
}
public override string ToString() {
StringBuilder sb = new StringBuilder();
sb.Append("(");
return ((ITuple)this).ToString(sb);
}
string ITuple.ToString(StringBuilder sb) {
sb.Append(m_Item1);
sb.Append(", ");
sb.Append(m_Item2);
sb.Append(", ");
sb.Append(m_Item3);
sb.Append(")");
return sb.ToString();
}
int ITuple.Size {
get {
return 3;
}
}
}
If your consuming code can make do with an IDictionary<> interface, instead of Dictionary, my instinct would have been to use a SortedDictionary<> with a custom array comparer, ie:
class ArrayComparer<T> : IComparer<IList<T>>
where T : IComparable<T>
{
public int Compare(IList<T> x, IList<T> y)
{
int compare = 0;
for (int n = 0; n < x.Count && n < y.Count; ++n)
{
compare = x[n].CompareTo(y[n]);
}
return compare;
}
}
And create thus (using int[] just for concrete example's sake):
var dictionary = new SortedDictionary<int[], string>(new ArrayComparer<int>());
참고URL : https://stackoverflow.com/questions/955982/tuples-or-arrays-as-dictionary-keys-in-c-sharp
'IT박스' 카테고리의 다른 글
img 요소를 만드는 데 가장 좋은 JavaScript 코드는 무엇입니까 (0) | 2020.08.15 |
---|---|
R의 ifelse 문이 벡터를 반환 할 수없는 이유는 무엇입니까? (0) | 2020.08.15 |
moment.js 24h 형식 (0) | 2020.08.14 |
'app : mergeDebugResources'작업 실행 실패 크 런처 크 런처… png 실패 (0) | 2020.08.14 |
여러 태그 도커 이미지 만들기 (0) | 2020.08.14 |