2009-03-26 35 views
18

Tôi thực sự muốn sử dụng băm trong chương trình của mình. Sử dụng một từ điển cảm thấy xấu xí. Tôi có lẽ sẽ bắt đầu sử dụng VS2008 với .Net 3.5 một số ngày, vì vậy lý tưởng của tôi sẽ được rằng mặc dù tôi không thể (hoặc có thể tôi?) Sử dụng hashsets trong VS2005, khi tôi bắt đầu sử dụng. NET 3.5, tôi không muốn phải thay đổi nhiều, nếu có, để chuyển sang sử dụng các hashsets này.Sử dụng HashSet trong C# 2.0, tương thích với 3.5

Tôi tự hỏi nếu có ai biết về một triển khai thực hiện hashset hiện có được thiết kế với ý nghĩ này hay cách sử dụng bộ băm 3.5 trong VS2005.

Trả lời

23

Bạn có thể sử dụng HashSet<T> trong ứng dụng 2.0 ngay bây giờ - chỉ cần tham chiếu System.Core.dll và bạn nên làm điều tốt.

Lưu ý: Điều này sẽ yêu cầu bạn cài đặt .NET 3.5 framework miễn phí và riêng biệt với Visual Studio. Khi bạn đã cài đặt xong, bạn sẽ có phiên bản System.Core mới chứa loại HashSet<T>. Kể từ khi các phiên bản .NET framework 2.0 - 3.5 tất cả chia sẻ cùng một CLR, bạn có thể sử dụng assembly này trong ứng dụng 2.0 của bạn mà không có bất kỳ vấn đề nào.

+1

Tuy nhiên, yêu cầu bạn phải sử dụng XP trở lên. Đó là vấn đề tôi gặp phải và tại sao tôi phải tạo HashSet của riêng mình. Chúng tôi phát triển trên XP và có được tất cả những thứ tuyệt vời của VS 2008, nhưng tất cả các khách hàng của chúng tôi đều sử dụng Win2K và không thể chạy .NET 3.5 framework, vì vậy chúng tôi phải nhắm mục tiêu .NET 2.0. –

+1

Tôi không thể giải nén HashSet từ System.Core và biên dịch nó thành assembly của tôi? –

+5

Có, bạn có thể nhưng điều đó không có nghĩa là bạn nên. Giấy phép của khuôn khổ nghiêm cấm điều này. –

1

Bạn có thể đặt tên cho Từ điển là Hashset bằng chỉ thị sử dụng. Không thực sự giống nhau, nhưng nó có thể đơn giản hóa mọi thứ cho bạn sau này.

2

Tôi nghĩ thư viện PowerCollections phải phù hợp với nhu cầu của bạn. Đó là một thư viện mã nguồn mở có chứa một số lớp sưu tập bị thiếu trong .NET, bao gồm Set<T>, Bag<T>, MultiDictionary v.v. Nó chạy trên .NET 2.0. Tôi đã sử dụng nó trong vài năm và tôi rất hài lòng với nó.

8

Bạn có thể sử dụng Iesi.Collections (được sử dụng bởi NHibernate) hoặc Mono's HashSet

+0

+! cho liên kết cho HashSet của Mono, đó chính xác là những gì tôi đang tìm kiếm ở đây từ Google – PiotrK

24

Đây là một trong những tôi đã viết cho 2.0 sử dụng một từ điển < T, đối tượng > nội bộ. Nó không khớp chính xác với 3.5 HashSet <T>, nhưng nó thực hiện công việc cho tôi.

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 
} 
+1

Ok, tấn công tôi nếu cần thiết, nhưng bạn thực sự đã đánh bại điểm của HashSet ở đây. Bạn đã thay thế chức năng của phương thức HashSet.Contains() bằng phương thức Dictionary.ConatinsKey(). Phương thức này là O (n) trong khi phương thức HashSet.Contains() là O (1). triển khai này có tất cả tốc độ của đối tượng Danh sách . – SamuelWarren

+0

@highphilosopher: Đây chỉ là một xấp xỉ nhanh chóng và bẩn của một HashSet .Những gì tôi thực sự làm, và những gì tôi đang làm bây giờ, vì tôi phải hỗ trợ người dùng trên Win2K trong khi sử dụng HashSet và các tính năng khác 3.5, là biên dịch lại System.Core của Mono 2.0 và phát triển trong VS 2008. Đoạn mã trên đã phục vụ mục đích của tôi trong vài tuần tôi đã sử dụng và không được sử dụng cho bất kỳ thứ gì ngoài các tập dữ liệu nhỏ. –

+14

@highphilosopher: Không chính xác. Đối với bản ghi, các phương thức khóa 'Dictionary <>' (bao gồm 'ConstainsKey') được băm O (1) triển khai và rất giống với các phương thức HashSet của một mục đích tương tự. 'ContainsValue' là O (n). Một trong những mục đích của từ điển là cung cấp tra cứu tốc độ cao cho các khóa, thay vì sử dụng một chỉ mục như một mảng. –

Các vấn đề liên quan