【问题标题】:Using HashSet in C# 2.0, compatible with 3.5在 C# 2.0 中使用 HashSet,兼容 3.5
【发布时间】:2009-03-26 18:35:10
【问题描述】:

我真的很想在我的程序中使用哈希集。使用字典感觉很难看。我可能有一天会开始使用 .Net 3.5 的 VS2008,所以我的理想是即使我不能(或者我可以?)在 VS2005 中使用 hashsets,但当我开始使用 .NET 3.5 时,我不会不想为了切换到使用这些哈希集而进行很多更改(如果有的话)。

我想知道是否有人知道考虑到这一点设计的现有哈希集实现,或者在 VS2005 中使用 3.5 哈希集的方法。

【问题讨论】:

    标签: c# .net visual-studio-2005


    【解决方案1】:

    这是我为 2.0 编写的,它在内部使用 Dictionary。它不是 3.5 HashSet 的完全匹配,但它为我完成了这项工作。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Runtime.Serialization;
    
    public class HashSet<T> : ICollection<T>, ISerializable, IDeserializationCallback
    {
        private readonly Dictionary<T, object> dict;
    
        public HashSet()
        {
            dict = new Dictionary<T, object>();
        }
    
        public HashSet(IEnumerable<T> items) : this()
        {
            if (items == null)
            {
                return;
            }
    
            foreach (T item in items)
            {
                Add(item);
            }
        }
    
        public HashSet<T> NullSet { get { return new HashSet<T>(); } }
    
        #region ICollection<T> Members
    
        public void Add(T item)
        {
            if (null == item)
            {
                throw new ArgumentNullException("item");
            }
    
            dict[item] = null;
        }
    
        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception>
        public void Clear()
        {
            dict.Clear();
        }
    
        public bool Contains(T item)
        {
            return dict.ContainsKey(item);
        }
    
        /// <summary>
        /// Copies the items of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the items copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception><exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.-or-<paramref name="arrayIndex"/> is equal to or greater than the length of <paramref name="array"/>.-or-The number of items in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.-or-Type T cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception>
        public void CopyTo(T[] array, int arrayIndex)
        {
            if (array == null) throw new ArgumentNullException("array");
            if (arrayIndex < 0 || arrayIndex >= array.Length || arrayIndex >= Count)
            {
                throw new ArgumentOutOfRangeException("arrayIndex");
            }
    
            dict.Keys.CopyTo(array, arrayIndex);
        }
    
        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <returns>
        /// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </returns>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
        public bool Remove(T item)
        {
            return dict.Remove(item);
        }
    
        /// <summary>
        /// Gets the number of items contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <returns>
        /// The number of items contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </returns>
        public int Count
        {
            get { return dict.Count; }
        }
    
        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
        /// </summary>
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
        /// </returns>
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }
    
        #endregion
    
        public HashSet<T> Union(HashSet<T> set)
        {
            HashSet<T> unionSet = new HashSet<T>(this);
    
            if (null == set)
            {
                return unionSet;
            }
    
            foreach (T item in set)
            {
                if (unionSet.Contains(item))
                {
                    continue;
                }
    
                unionSet.Add(item);
            }
    
            return unionSet;
        }
    
        public HashSet<T> Subtract(HashSet<T> set)
        {
            HashSet<T> subtractSet = new HashSet<T>(this);
    
            if (null == set)
            {
                return subtractSet;
            }
    
            foreach (T item in set)
            {
                if (!subtractSet.Contains(item))
                {
                    continue;
                }
    
                subtractSet.dict.Remove(item);
            }
    
            return subtractSet;
        }
    
        public bool IsSubsetOf(HashSet<T> set)
        {
            HashSet<T> setToCompare = set ?? NullSet;
    
            foreach (T item in this)
            {
                if (!setToCompare.Contains(item))
                {
                    return false;
                }
            }
    
            return true;
        }
    
        public HashSet<T> Intersection(HashSet<T> set)
        {
            HashSet<T> intersectionSet = NullSet;
    
            if (null == set)
            {
                return intersectionSet;
            }
    
            foreach (T item in this)
            {
                if (!set.Contains(item))
                {
                    continue;
                }
    
                intersectionSet.Add(item);
            }
    
            foreach (T item in set)
            {
                if (!Contains(item) || intersectionSet.Contains(item))
                {
                    continue;
                }
    
                intersectionSet.Add(item);
            }
    
            return intersectionSet;
        }
    
        public bool IsProperSubsetOf(HashSet<T> set)
        {
            HashSet<T> setToCompare = set ?? NullSet;
    
            // A is a proper subset of a if the b is a subset of a and a != b
            return (IsSubsetOf(setToCompare) && !setToCompare.IsSubsetOf(this));
        }
    
        public bool IsSupersetOf(HashSet<T> set)
        {
            HashSet<T> setToCompare = set ?? NullSet;
    
            foreach (T item in setToCompare)
            {
                if (!Contains(item))
                {
                    return false;
                }
            }
    
            return true;
        }
    
        public bool IsProperSupersetOf(HashSet<T> set)
        {
            HashSet<T> setToCompare = set ?? NullSet;
    
            // B is a proper superset of a if b is a superset of a and a != b
            return (IsSupersetOf(setToCompare) && !setToCompare.IsSupersetOf(this));
        }
    
        public List<T> ToList()
        {
            return new List<T>(this);
        }
    
        #region Implementation of ISerializable
    
        /// <summary>
        /// Populates a <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with the data needed to serialize the target object.
        /// </summary>
        /// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to populate with data. </param><param name="context">The destination (see <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization. </param><exception cref="T:System.Security.SecurityException">The caller does not have the required permission. </exception>
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info == null) throw new ArgumentNullException("info");
            dict.GetObjectData(info, context);
        }
    
        #endregion
    
        #region Implementation of IDeserializationCallback
    
        /// <summary>
        /// Runs when the entire object graph has been deserialized.
        /// </summary>
        /// <param name="sender">The object that initiated the callback. The functionality for this parameter is not currently implemented. </param>
        public void OnDeserialization(object sender)
        {
            dict.OnDeserialization(sender);
        }
    
        #endregion
    
        #region Implementation of IEnumerable
    
        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        public IEnumerator<T> GetEnumerator()
        {
            return dict.Keys.GetEnumerator();
        }
    
        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        #endregion
    }
    

    【讨论】:

    • @highphilosopher:这只是 HashSet 的快速而肮脏的近似。我实际上要做的,以及我现在正在做的,因为我必须在使用 HashSets 和其他 3.5 好东西的同时支持 Win2K 上的用户,是为 2.0 重新编译 Mono 的 System.Core 并在 VS 2008 中开发. 上面的代码在我使用它的几周内服务于我的目的,除了小数据集之外,它不能用于任何用途。
    • @highphilosopher:这是不正确的。作为记录,Dictionary&lt;&gt; 关键方法(包括ConstainsKey)是经过哈希处理的 O(1) 实现,与具有类似目的的 HashSet 方法非常相似。 ContainsValue 是 O(n)。 Dictionary 的目的之一是提供对键的高速查找,而不是将索引用作数组。
    • 凯文,谢谢你打倒我 :) 我不喜欢那个。
    【解决方案2】:

    您现在可以在 2.0 应用程序中使用 HashSet&lt;T&gt; - 只需引用 System.Core.dll 就可以了。

    注意:这需要您安装.NET 3.5 framework,它是免费的,独立于 Visual Studio。安装完成后,您将拥有包含 HashSet&lt;T&gt; 类型的新 System.Core 程序集。由于 .NET 框架版本 2.0 - 3.5 都共享相同的 CLR,因此您可以在 2.0 应用程序中使用此程序集而不会出现任何问题。

    【讨论】:

    • 不过,它确实要求您使用 XP 或更高版本。这就是我遇到的问题,也是我必须创建自己的 HashSet 的原因。我们在 XP 上开发,并获得了所有出色的 VS 2008 东西,但我们所有的客户端都在 Win2K 上,并且无法运行 .NET 3.5 框架,因此我们必须以 .NET 2.0 为目标。
    • 我不能从 System.Core 中提取 HashSet 并将其编译到我的程序集中吗?
    • 是的,您可以,但这并不意味着您应该这样做。框架的许可证禁止这样做。
    • 我试过使用它,它确实有效。虽然为了分发这样的应用程序,您需要确保用户具有 .Net Framework 3.5 或需要在应用程序中包含 System.Core DLL(默认情况下,system.core 不会输出到发布目录)。
    • 你也可以使用 Mono 的 HashSet。
    【解决方案3】:

    您可以使用Iesi.Collections(NHibernate 使用)或Mono's HashSet

    【讨论】:

    • +!获取 Mono 的 HashSet 的链接,这正是我想要从 Google 来到这里的内容
    【解决方案4】:

    C5 Library 也有一个 HashSet 实现。

    【讨论】:

      【解决方案5】:

      我认为PowerCollections 库应该满足您的需求。它是一个开源库,包含 .NET 中缺少的几个集合类,包括 Set&lt;T&gt;Bag&lt;T&gt;MultiDictionary 等。它在 .NET 2.0 上运行。我已经使用它几年了,我对它非常满意。

      【讨论】:

        【解决方案6】:

        您可以使用 using 指令将 Dictionary 别名为 Hashset。不是一回事,但它可能会在以后为您简化事情。

        【讨论】:

          猜你喜欢
          • 2010-11-30
          • 2011-02-18
          • 2012-11-08
          • 1970-01-01
          • 2011-04-07
          • 1970-01-01
          • 2010-09-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多