왜 불가능한 지에 대한 이 질문을 읽었 지만 문제에 대한 해결책을 찾지 못했습니다.
.NET에서 항목을 검색하고 싶습니다 HashSet<T>
. 이 서명이있는 방법을 찾고 있습니다.
/// <summary>
/// Determines if this set contains an item equal to <paramref name="item"/>,
/// according to the comparison mechanism that was used when the set was created.
/// The set is not changed. If the set does contain an item equal to
/// <paramref name="item"/>, then the item from the set is returned.
/// </summary>
bool TryGetItem<T>(T item, out T foundItem);
이러한 방법으로 항목 집합을 검색하면 O (1)이됩니다. a에서 항목을 검색하는 유일한 방법 HashSet<T>
은 O (n) 인 모든 항목을 열거하는 것입니다.
본인은이 문제 다음 내 자신을 만들기 위해 어떤 해결 방법을 찾을 수있다 HashSet<T>
또는를 사용합니다 Dictionary<K, V>
. 다른 생각은 없나요?
참고 : 에 항목
이 HashSet<T>
포함되어 있는지 확인하고 싶지 않습니다 . 항목 HashSet<T>
을 업데이트해야하므로에 저장된 항목에 대한 참조를 가져오고 싶습니다 (다른 인스턴스로 대체하지 않음). 내가 전달할 항목은 TryGetItem
동일하지만 (생성자에게 전달한 비교 메커니즘에 따라) 동일한 참조가 아닙니다.
답변
요청하신 내용은 1 년 전에 .NET Core 에 추가 되었으며 최근 .NET 4.7.2에 추가되었습니다 .
.NET Framework 4.7.2에서는 다음과 같이 새로운 기능을 활성화하는 몇 가지 API를 표준 컬렉션 유형에 추가했습니다.
– ‘TryGetValue’가 SortedSet 및 HashSet에 추가되어 다른 컬렉션 유형에서 사용되는 Try 패턴과 일치합니다.
서명은 다음과 같습니다 (.NET 4.7.2 이상에 있음).
//
// Summary:
// Searches the set for a given value and returns the equal value it finds, if any.
//
// Parameters:
// equalValue:
// The value to search for.
//
// actualValue:
// The value from the set that the search found, or the default value of T when
// the search yielded no match.
//
// Returns:
// A value indicating whether the search was successful.
public bool TryGetValue(T equalValue, out T actualValue);
추신 : 관심이 있으시면 앞으로 추가 할 관련 기능이 있습니다 -HashSet.GetOrAdd (T).
답변
이것은 실제로 컬렉션 세트에서 큰 누락입니다. 키 사전 또는 객체 참조 검색을 허용하는 HashSet이 필요합니다. 너무 많은 사람들이 그것을 요구 해왔고, 왜 그것이 고쳐지지 않는지는 저를 넘어선 것입니다.
타사 라이브러리가 없으면 Dictionary<T, T>
Dictionary가 항목을 해시 테이블로 저장하기 때문에 값과 동일한 키 를 사용하는 것이 가장 좋은 해결 방법입니다 . 성능면에서는 HashSet과 동일하지만 물론 메모리를 낭비합니다 (항목 당 포인터 크기).
Dictionary<T, T> myHashedCollection;
...
if(myHashedCollection.ContainsKey[item])
item = myHashedCollection[item]; //replace duplicate
else
myHashedCollection.Add(item, item); //add previously unknown item
...
//work with unique item
답변
이 메서드는 .NET Framework 4.7.2 (및 이전 .NET Core 2.0 )에 추가되었습니다. 참조하십시오 HashSet<T>.TryGetValue
. 출처 인용 :
/// <summary>
/// Searches the set for a given value and returns the equal value it finds, if any.
/// </summary>
/// <param name="equalValue">The value to search for.
/// </param>
/// <param name="actualValue">
/// The value from the set that the search found, or the default value
/// of <typeparamref name="T"/> when the search yielded no match.</param>
/// <returns>A value indicating whether the search was successful.</returns>
/// <remarks>
/// This can be useful when you want to reuse a previously stored reference instead of
/// a newly constructed one (so that more sharing of references can occur) or to look up
/// a value that has more complete data than the value you currently have, although their
/// comparer functions indicate they are equal.
/// </remarks>
public bool TryGetValue(T equalValue, out T actualValue)
답변
문자열 같음 비교자를 오버로드하는 것은 어떻습니까?
class StringEqualityComparer : IEqualityComparer<String>
{
public string val1;
public bool Equals(String s1, String s2)
{
if (!s1.Equals(s2)) return false;
val1 = s1;
return true;
}
public int GetHashCode(String s)
{
return s.GetHashCode();
}
}
public static class HashSetExtension
{
public static bool TryGetValue(this HashSet<string> hs, string value, out string valout)
{
if (hs.Contains(value))
{
valout=(hs.Comparer as StringEqualityComparer).val1;
return true;
}
else
{
valout = null;
return false;
}
}
}
그런 다음 HashSet을 다음과 같이 선언하십시오.
HashSet<string> hs = new HashSet<string>(new StringEqualityComparer());
답변
자, 이렇게 할 수 있습니다
YourObject x = yourHashSet.Where(w => w.Name.Contains("strin")).FirstOrDefault();
선택한 개체의 새 인스턴스를 가져 오는 것입니다. 개체를 업데이트하려면 다음을 사용해야합니다.
yourHashSet.Where(w => w.Name.Contains("strin")).FirstOrDefault().MyProperty = "something";
답변
이제 .NET Core 2.0에는이 정확한 방법이 있습니다.
답변
또 다른 트릭은 InternalIndexOf
HashSet 의 내부 기능에 액세스하여 Reflection을 수행 합니다. 필드 이름은 하드 코딩되므로 향후 .NET 버전에서 변경되면 중단됩니다.
참고 : Mono를 사용하는 경우 필드 이름을에서 m_slots
로 변경해야 합니다 _slots
.
internal static class HashSetExtensions<T>
{
public delegate bool GetValue(HashSet<T> source, T equalValue, out T actualValue);
public static GetValue TryGetValue { get; }
static HashSetExtensions() {
var targetExp = Expression.Parameter(typeof(HashSet<T>), "target");
var itemExp = Expression.Parameter(typeof(T), "item");
var actualValueExp = Expression.Parameter(typeof(T).MakeByRefType(), "actualValueExp");
var indexVar = Expression.Variable(typeof(int), "index");
// ReSharper disable once AssignNullToNotNullAttribute
var indexExp = Expression.Call(targetExp, typeof(HashSet<T>).GetMethod("InternalIndexOf", BindingFlags.NonPublic | BindingFlags.Instance), itemExp);
var truePart = Expression.Block(
Expression.Assign(
actualValueExp, Expression.Field(
Expression.ArrayAccess(
// ReSharper disable once AssignNullToNotNullAttribute
Expression.Field(targetExp, typeof(HashSet<T>).GetField("m_slots", BindingFlags.NonPublic | BindingFlags.Instance)), indexVar),
"value")),
Expression.Constant(true));
var falsePart = Expression.Constant(false);
var block = Expression.Block(
new[] { indexVar },
Expression.Assign(indexVar, indexExp),
Expression.Condition(
Expression.GreaterThanOrEqual(indexVar, Expression.Constant(0)),
truePart,
falsePart));
TryGetValue = Expression.Lambda<GetValue>(block, targetExp, itemExp, actualValueExp).Compile();
}
}
public static class Extensions
{
public static bool TryGetValue2<T>(this HashSet<T> source, T equalValue, out T actualValue) {
if (source.Count > 0) {
if (HashSetExtensions<T>.TryGetValue(source, equalValue, out actualValue)) {
return true;
}
}
actualValue = default;
return false;
}
}
테스트:
var x = new HashSet<int> { 1, 2, 3 };
if (x.TryGetValue2(1, out var value)) {
Console.WriteLine(value);
}