จะดึงรายการจริงจาก HashSet <T> ได้อย่างไร?


86

ฉันได้อ่านคำถามนี้เกี่ยวกับสาเหตุที่เป็นไปไม่ได้ แต่ยังไม่พบวิธีแก้ปัญหา

ผมอยากจะดึงรายการจาก HashSet<T>.NET ฉันกำลังมองหาวิธีที่จะมีลายเซ็นนี้:

/// <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
ทำไมไม่ใช้มีและส่งคืนรายการที่คุณส่งผ่านเป็นอินพุต?
Mathias


2
หากคุณต้องการค้นหาวัตถุตามค่าคีย์ Dictionary <T> อาจเป็นคอลเล็กชันที่เหมาะสมกว่าในการจัดเก็บ
ThatBlairGuy

@ThatBlairGuy: คุณพูดถูก ฉันคิดว่าฉันจะใช้ชุดคอลเลกชันของตัวเองโดยใช้พจนานุกรมภายในเพื่อจัดเก็บรายการของฉัน คีย์จะเป็น HashCode ของรายการ ฉันจะมีประสิทธิภาพใกล้เคียงกับ HashSet และจะช่วยให้ฉันไม่ต้องระบุรหัสทุกครั้งที่ฉันต้องเพิ่ม / ลบ / รับไอเท็มจากคอลเล็กชันของฉัน
Francois C

2
@mathias เนื่องจากแฮชเซ็ตอาจมีรายการที่เท่ากับอินพุต แต่ไม่เหมือนกันจริงๆ ตัวอย่างเช่นคุณอาจต้องการมีแฮชของประเภทการอ้างอิง แต่คุณต้องการเปรียบเทียบเนื้อหาไม่ใช่การอ้างอิงเพื่อความเท่าเทียมกัน
NounVerber

คำตอบ:


28

สิ่งที่คุณขอถูกบันทึกอยู่ใน.NET หลักปีที่ผ่านมาและได้รับการเพิ่มเร็ว ๆ นี้เพื่อ .NET 4.7.2 :

ใน. NET Framework 4.7.2 เราได้เพิ่ม API สองสามตัวในประเภทคอลเล็กชันมาตรฐานซึ่งจะเปิดใช้งานฟังก์ชันใหม่ดังนี้
- 'TryGetValue' ถูกเพิ่มใน SortedSet และ HashSet เพื่อให้ตรงกับรูปแบบการลองที่ใช้ในคอลเลกชันประเภทอื่น

ลายเซ็นมีดังนี้ (พบใน. 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);

PS .: ในกรณีที่คุณสนใจมีฟังก์ชันที่เกี่ยวข้องซึ่งจะเพิ่มเข้ามาในอนาคต - HashSet.GetOrAdd (T)


65

นี่เป็นการละเว้นอย่างมากในชุดคอลเลกชัน คุณจะต้องมีพจนานุกรมคีย์เท่านั้นหรือ 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

1
ฉันขอแนะนำว่ากุญแจสำคัญในพจนานุกรมของเขาควรเป็นสิ่งที่เขาวางไว้ใน EqualityComparer สำหรับแฮชเซ็ต ฉันรู้สึกว่ามันสกปรกที่จะใช้ EqualityComparer เมื่อคุณไม่ได้บอกว่ารายการนั้นเท่ากันจริงๆ (มิฉะนั้นคุณสามารถใช้รายการที่คุณสร้างขึ้นเพื่อจุดประสงค์ในการเปรียบเทียบ) ฉันจะสร้างคลาส / โครงสร้างที่แสดงถึงคีย์ สิ่งนี้มาพร้อมกับค่าหน่วยความจำที่มากขึ้นแน่นอน
Ed T

1
เนื่องจากคีย์ถูกเก็บไว้ใน Value ฉันขอแนะนำให้ใช้คอลเล็กชันที่สืบทอดมาจาก KeyedCollection แทน Dictionary msdn.microsoft.com/en-us/library/ms132438(v=vs.110).aspx
การเข้าถึงถูกปฏิเสธใน

11

วิธีนี้ได้ถูกเพิ่มลงใน. 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)

1
เช่นเดียวกับSortedSetเช่นกัน
nawfal

4

สิ่งที่เกี่ยวกับการโอเวอร์โหลดตัวเปรียบเทียบความเท่าเทียมกันของสตริง:

  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());

ทั้งหมดนี้เกี่ยวกับการจัดการหน่วยความจำ - ส่งคืนรายการจริงที่อยู่ในแฮชเซ็ตแทนที่จะเป็นสำเนาที่เหมือนกัน ดังนั้นในโค้ดด้านบนเราจะพบสตริงที่มีเนื้อหาเหมือนกันแล้วส่งคืนการอ้างอิงถึง สำหรับสตริงสิ่งนี้คล้ายกับสิ่งที่ฝึกงาน
mp666

@zumalifeguard @ mp666 นี้ไม่รับประกันว่าจะทำงานตามที่เป็นอยู่ จะต้องมีคนสร้างอินสแตนซ์HashSetเพื่อจัดหาเครื่องมือแปลงค่าที่เฉพาะเจาะจง ทางออกที่ดีที่สุดคือTryGetValueการส่งผ่านอินสแตนซ์ใหม่ของผู้เชี่ยวชาญStringEqualityComparer(มิฉะนั้นas StringEqualityComparerอาจส่งผลให้เกิดโมฆะทำให้การ.val1เข้าถึงคุณสมบัติโยน) ในการทำเช่นนั้น StringEqualityComparer สามารถกลายเป็นคลาสส่วนตัวที่ซ้อนกันภายใน HashSetExtension ยิ่งไปกว่านั้นในกรณีของตัวเปรียบเทียบความเท่าเทียมที่ถูกลบล้าง StringEqualityComparer ควรเรียกใช้เป็นค่าเริ่มต้น
Graeme Wicksted

คุณต้องประกาศ HashSet ของคุณเป็น: HashSet <string> valueCash = new HashSet <string> (ใหม่ StringEqualityComparer ())
mp666

1
แฮ็คสกปรก ฉันรู้ว่ามันทำงานอย่างไร แต่มันขี้เกียจแค่ทำให้มันใช้งานได้
M.kazem Akhgary

2

โอเคคุณทำได้แบบนี้

YourObject x = yourHashSet.Where(w => w.Name.Contains("strin")).FirstOrDefault();

นี่คือการรับอินสแตนซ์ใหม่ของวัตถุที่เลือก ในการอัปเดตวัตถุของคุณคุณควรใช้:

yourHashSet.Where(w => w.Name.Contains("strin")).FirstOrDefault().MyProperty = "something";

นี่เป็นวิธีที่น่าสนใจเพียงแค่คุณต้องลองใช้ครั้งที่สอง - ดังนั้นหากคุณค้นหาสิ่งที่ไม่อยู่ในรายการคุณจะได้รับ NullReferenceExpection แต่มันเป็นขั้นตอนในทิศทางที่ถูกต้อง?
Piotr Kula

11
LINQ ข้ามคอลเลกชันใน foreach loop เช่น O (n) เวลาค้นหา แม้ว่าจะเป็นการแก้ปัญหา แต่ก็เป็นการเอาชนะจุดประสงค์ของการใช้ HashSet ตั้งแต่แรก
Niklas Ekman


2

เคล็ดลับอื่นจะทำ Reflection โดยการเข้าถึงฟังก์ชันภายในInternalIndexOfของ HashSet โปรดทราบว่าชื่อฟิลด์เป็นฮาร์ดโค้ดดังนั้นหากการเปลี่ยนแปลงเหล่านั้นใน. 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);
}


1

ปรับเปลี่ยนการใช้งานคำตอบ @ mp666 เพื่อให้สามารถใช้กับ HashSet ประเภทใดก็ได้และอนุญาตให้ลบล้างตัวเปรียบเทียบความเท่าเทียมเริ่มต้น

public interface IRetainingComparer<T> : IEqualityComparer<T>
{
    T Key { get; }
    void ClearKeyCache();
}

/// <summary>
/// An <see cref="IEqualityComparer{T}"/> that retains the last key that successfully passed <see cref="IEqualityComparer{T}.Equals(T,T)"/>.
/// This class relies on the fact that <see cref="HashSet{T}"/> calls the <see cref="IEqualityComparer{T}.Equals(T,T)"/> with the first parameter
/// being an existing element and the second parameter being the one passed to the initiating call to <see cref="HashSet{T}"/> (eg. <see cref="HashSet{T}.Contains(T)"/>).
/// </summary>
/// <typeparam name="T">The type of object being compared.</typeparam>
/// <remarks>This class is thread-safe but may should not be used with any sort of parallel access (PLINQ).</remarks>
public class RetainingEqualityComparerObject<T> : IRetainingComparer<T> where T : class
{
    private readonly IEqualityComparer<T> _comparer;

    [ThreadStatic]
    private static WeakReference<T> _retained;

    public RetainingEqualityComparerObject(IEqualityComparer<T> comparer)
    {
        _comparer = comparer;
    }

    /// <summary>
    /// The retained instance on side 'a' of the <see cref="Equals"/> call which successfully met the equality requirement agains side 'b'.
    /// </summary>
    /// <remarks>Uses a <see cref="WeakReference{T}"/> so unintended memory leaks are not encountered.</remarks>
    public T Key
    {
        get
        {
            T retained;
            return _retained == null ? null : _retained.TryGetTarget(out retained) ? retained : null;
        }
    }


    /// <summary>
    /// Sets the retained <see cref="Key"/> to the default value.
    /// </summary>
    /// <remarks>This should be called prior to performing an operation that calls <see cref="Equals"/>.</remarks>
    public void ClearKeyCache()
    {
        _retained = _retained ?? new WeakReference<T>(null);
        _retained.SetTarget(null);
    }

    /// <summary>
    /// Test two objects of type <see cref="T"/> for equality retaining the object if successful.
    /// </summary>
    /// <param name="a">An instance of <see cref="T"/>.</param>
    /// <param name="b">A second instance of <see cref="T"/> to compare against <paramref name="a"/>.</param>
    /// <returns>True if <paramref name="a"/> and <paramref name="b"/> are equal, false otherwise.</returns>
    public bool Equals(T a, T b)
    {
        if (!_comparer.Equals(a, b))
        {
            return false;
        }

        _retained = _retained ?? new WeakReference<T>(null);
        _retained.SetTarget(a);
        return true;
    }

    /// <summary>
    /// Gets the hash code value of an instance of <see cref="T"/>.
    /// </summary>
    /// <param name="o">The instance of <see cref="T"/> to obtain a hash code from.</param>
    /// <returns>The hash code value from <paramref name="o"/>.</returns>
    public int GetHashCode(T o)
    {
        return _comparer.GetHashCode(o);
    }
}

/// <summary>
/// An <see cref="IEqualityComparer{T}"/> that retains the last key that successfully passed <see cref="IEqualityComparer{T}.Equals(T,T)"/>.
/// This class relies on the fact that <see cref="HashSet{T}"/> calls the <see cref="IEqualityComparer{T}.Equals(T,T)"/> with the first parameter
/// being an existing element and the second parameter being the one passed to the initiating call to <see cref="HashSet{T}"/> (eg. <see cref="HashSet{T}.Contains(T)"/>).
/// </summary>
/// <typeparam name="T">The type of object being compared.</typeparam>
/// <remarks>This class is thread-safe but may should not be used with any sort of parallel access (PLINQ).</remarks>
public class RetainingEqualityComparerStruct<T> : IRetainingComparer<T> where T : struct 
{
    private readonly IEqualityComparer<T> _comparer;

    [ThreadStatic]
    private static T _retained;

    public RetainingEqualityComparerStruct(IEqualityComparer<T> comparer)
    {
        _comparer = comparer;
    }

    /// <summary>
    /// The retained instance on side 'a' of the <see cref="Equals"/> call which successfully met the equality requirement agains side 'b'.
    /// </summary>
    public T Key => _retained;


    /// <summary>
    /// Sets the retained <see cref="Key"/> to the default value.
    /// </summary>
    /// <remarks>This should be called prior to performing an operation that calls <see cref="Equals"/>.</remarks>
    public void ClearKeyCache()
    {
        _retained = default(T);
    }

    /// <summary>
    /// Test two objects of type <see cref="T"/> for equality retaining the object if successful.
    /// </summary>
    /// <param name="a">An instance of <see cref="T"/>.</param>
    /// <param name="b">A second instance of <see cref="T"/> to compare against <paramref name="a"/>.</param>
    /// <returns>True if <paramref name="a"/> and <paramref name="b"/> are equal, false otherwise.</returns>
    public bool Equals(T a, T b)
    {
        if (!_comparer.Equals(a, b))
        {
            return false;
        }

        _retained = a;
        return true;
    }

    /// <summary>
    /// Gets the hash code value of an instance of <see cref="T"/>.
    /// </summary>
    /// <param name="o">The instance of <see cref="T"/> to obtain a hash code from.</param>
    /// <returns>The hash code value from <paramref name="o"/>.</returns>
    public int GetHashCode(T o)
    {
        return _comparer.GetHashCode(o);
    }
}

/// <summary>
/// Provides TryGetValue{T} functionality similar to that of <see cref="IDictionary{TKey,TValue}"/>'s implementation.
/// </summary>
public class ExtendedHashSet<T> : HashSet<T>
{
    /// <summary>
    /// This class is guaranteed to wrap the <see cref="IEqualityComparer{T}"/> with one of the <see cref="IRetainingComparer{T}"/>
    /// implementations so this property gives convenient access to the interfaced comparer.
    /// </summary>
    private IRetainingComparer<T> RetainingComparer => (IRetainingComparer<T>)Comparer;

    /// <summary>
    /// Creates either a <see cref="RetainingEqualityComparerStruct{T}"/> or <see cref="RetainingEqualityComparerObject{T}"/>
    /// depending on if <see cref="T"/> is a reference type or a value type.
    /// </summary>
    /// <param name="comparer">(optional) The <see cref="IEqualityComparer{T}"/> to wrap. This will be set to <see cref="EqualityComparer{T}.Default"/> if none provided.</param>
    /// <returns>An instance of <see cref="IRetainingComparer{T}"/>.</returns>
    private static IRetainingComparer<T> Create(IEqualityComparer<T> comparer = null)
    {
        return (IRetainingComparer<T>) (typeof(T).IsValueType ? 
            Activator.CreateInstance(typeof(RetainingEqualityComparerStruct<>)
                .MakeGenericType(typeof(T)), comparer ?? EqualityComparer<T>.Default)
            :
            Activator.CreateInstance(typeof(RetainingEqualityComparerObject<>)
                .MakeGenericType(typeof(T)), comparer ?? EqualityComparer<T>.Default));
    }

    public ExtendedHashSet() : base(Create())
    {
    }

    public ExtendedHashSet(IEqualityComparer<T> comparer) : base(Create(comparer))
    {
    }

    public ExtendedHashSet(IEnumerable<T> collection) : base(collection, Create())
    {
    }

    public ExtendedHashSet(IEnumerable<T> collection, IEqualityComparer<T> comparer) : base(collection, Create(comparer))
    {
    }

    /// <summary>
    /// Attempts to find a key in the <see cref="HashSet{T}"/> and, if found, places the instance in <paramref name="original"/>.
    /// </summary>
    /// <param name="value">The key used to search the <see cref="HashSet{T}"/>.</param>
    /// <param name="original">
    /// The matched instance from the <see cref="HashSet{T}"/> which is not neccessarily the same as <paramref name="value"/>.
    /// This will be set to null for reference types or default(T) for value types when no match found.
    /// </param>
    /// <returns>True if a key in the <see cref="HashSet{T}"/> matched <paramref name="value"/>, False if no match found.</returns>
    public bool TryGetValue(T value, out T original)
    {
        var comparer = RetainingComparer;
        comparer.ClearKeyCache();

        if (Contains(value))
        {
            original = comparer.Key;
            return true;
        }

        original = default(T);
        return false;
    }
}

public static class HashSetExtensions
{
    /// <summary>
    /// Attempts to find a key in the <see cref="HashSet{T}"/> and, if found, places the instance in <paramref name="original"/>.
    /// </summary>
    /// <param name="hashSet">The instance of <see cref="HashSet{T}"/> extended.</param>
    /// <param name="value">The key used to search the <see cref="HashSet{T}"/>.</param>
    /// <param name="original">
    /// The matched instance from the <see cref="HashSet{T}"/> which is not neccessarily the same as <paramref name="value"/>.
    /// This will be set to null for reference types or default(T) for value types when no match found.
    /// </param>
    /// <returns>True if a key in the <see cref="HashSet{T}"/> matched <paramref name="value"/>, False if no match found.</returns>
    /// <exception cref="ArgumentNullException">If <paramref name="hashSet"/> is null.</exception>
    /// <exception cref="ArgumentException">
    /// If <paramref name="hashSet"/> does not have a <see cref="HashSet{T}.Comparer"/> of type <see cref="IRetainingComparer{T}"/>.
    /// </exception>
    public static bool TryGetValue<T>(this HashSet<T> hashSet, T value, out T original)
    {
        if (hashSet == null)
        {
            throw new ArgumentNullException(nameof(hashSet));
        }

        if (hashSet.Comparer.GetType().IsInstanceOfType(typeof(IRetainingComparer<T>)))
        {
            throw new ArgumentException($"HashSet must have an equality comparer of type '{nameof(IRetainingComparer<T>)}' to use this functionality", nameof(hashSet));
        }

        var comparer = (IRetainingComparer<T>)hashSet.Comparer;
        comparer.ClearKeyCache();

        if (hashSet.Contains(value))
        {
            original = comparer.Key;
            return true;
        }

        original = default(T);
        return false;
    }
}

1
เนื่องจากคุณใช้วิธีการขยาย Linq Enumerable.Containsมันจะแจกแจงองค์ประกอบทั้งหมดของชุดและเปรียบเทียบทำให้สูญเสียประโยชน์ใด ๆ จากการใช้งานแฮชของชุดนี้ จากนั้นคุณก็อาจเขียนset.SingleOrDefault(e => set.Comparer.Equals(e, obj))ซึ่งมีลักษณะการทำงานและประสิทธิภาพเช่นเดียวกับโซลูชันของคุณ
Daniel AA Pelsmaeker

@Virtlink จับดี - คุณพูดถูกจริงๆ ฉันจะแก้ไขคำตอบของฉัน
Graeme Wicksted

อย่างไรก็ตามหากคุณต้องรวม HashSet ที่ใช้ตัวเปรียบเทียบของคุณภายในก็จะใช้ได้ ดังนี้Utillib / ExtHashSet
Daniel AA Pelsmaeker

@Virtlink ขอบคุณ! ฉันลงเอยด้วยการรวม HashSet เป็นตัวเลือกหนึ่ง แต่ให้ตัวเปรียบเทียบและวิธีการขยายเพื่อความคล่องตัวเพิ่มเติม ตอนนี้ปลอดภัยต่อเธรดและจะไม่รั่วไหลของหน่วยความจำ ... แต่มันค่อนข้างมีรหัสมากกว่าที่ฉันคาดไว้!
Graeme Wicksted

@ Francois การเขียนโค้ดด้านบนเป็นการออกกำลังกายในการหาวิธีแก้ปัญหาเวลา / หน่วยความจำที่ "เหมาะสมที่สุด" อย่างไรก็ตามฉันไม่แนะนำให้คุณใช้วิธีนี้ การใช้พจนานุกรม <T, T> กับ IEqualityComparer ที่กำหนดเองนั้นตรงไปตรงมาและพิสูจน์ได้ในอนาคต!
Graeme Wicksted

-2

HashSet มีประกอบด้วย (T)วิธีการ

คุณสามารถระบุIEqualityComparer ได้หากคุณต้องการวิธีการเปรียบเทียบแบบกำหนดเอง (เช่นเก็บวัตถุบุคคล แต่ใช้ SSN เพื่อเปรียบเทียบความเท่าเทียมกัน)


-11

คุณยังสามารถใช้ ToList () วิธีการและใช้ตัวสร้างดัชนีกับสิ่งนั้น

HashSet<string> mySet = new HashSet();
mySet.Add("mykey");
string key = mySet.toList()[0];

ฉันไม่แน่ใจว่าทำไมคุณถึงลงคะแนนเมื่อฉันใช้ตรรกะนี้มันได้ผล ฉันต้องการดึงค่าจากโครงสร้างที่ขึ้นต้นด้วย Dictionary <string, ISet <String>> โดยที่ ISet มี x จำนวนค่า วิธีที่ตรงที่สุดในการรับค่าเหล่านั้นคือการวนผ่านพจนานุกรมโดยดึงคีย์และค่า ISet จากนั้นฉันวนลูปผ่าน ISet เพื่อแสดงค่าแต่ละค่า มันไม่สง่างาม แต่มันใช้งานได้
j.hull
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.