Koleksi yang hanya mengizinkan item unik di .NET?

103

Apakah ada koleksi di C # yang tidak memungkinkan Anda menambahkan item duplikat ke dalamnya? Misalnya, dengan kelas konyol

public class Customer {
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }

    public override int GetHashCode() {
        return (FirstName + LastName + Address).GetHashCode();
    }

    public override bool Equals(object obj) {
        Customer C = obj as Customer;
        return C != null && String.Equals(this.FirstName, C.FirstName) && String.Equals(this.LastName, C.LastName) && String.Equals(this.Address, C.Address);
    }
}

Kode berikut (jelas) akan memunculkan pengecualian:

Customer Adam = new Customer { Address = "A", FirstName = "Adam", LastName = "" };
Customer AdamDup = new Customer { Address = "A", FirstName = "Adam", LastName = "" };

Dictionary<Customer, bool> CustomerHash = new Dictionary<Customer, bool>();
CustomerHash.Add(Adam, true);
CustomerHash.Add(AdamDup, true);

Tetapi apakah ada kelas yang juga akan menjamin keunikan, tetapi tanpa KeyValuePairs? Saya pikir HashSet<T>akan melakukan itu, tetapi setelah membaca dokumen tampaknya kelas itu hanya implementasi set ( go figure ).

Adam Rackis
sumber
4
Saya tidak mengerti masalah Anda dengan HashSet<T>. MSDN mengatakan "Kelas HashSet <T> menyediakan operasi set berkinerja tinggi. Satu set adalah kumpulan yang tidak berisi elemen duplikat, dan elemennya tidak dalam urutan tertentu."
Daniel Hilgarth
5
Bisakah Anda menjelaskan lebih lanjut mengapa HashSet<T>tidak cukup?
JaredPar
@ bootinator: Dictionary<K,V>Kelas tidak menjamin segala jenis urutan.
LukeH
3
Saya kira dia hanya ingin melempar pengecualian ketika Anda mencoba menambahkan nilai yang sudah ada ... Untuk melakukan ini, cukup periksa nilai bool yang dikembalikan dari HashSet<T>.Addmetode dan lempar ketika false...
digEmAll
2
Juga sangat disarankan untuk hanya membebani mereka untuk tipe yang tidak dapat diubah saja. Pelanggan yang bisa berubah biasanya akan lebih baik dengan persamaan Referensi default.
Henk Holterman

Jawaban:

205

HashSet<T>adalah apa yang Anda cari. Dari MSDN (penekanan ditambahkan):

The HashSet<T>kelas menyediakan operasi set kinerja tinggi. Satu set adalah kumpulan yang tidak berisi elemen duplikat , dan yang elemennya tidak berada dalam urutan tertentu.

Perhatikan bahwa HashSet<T>.Add(T item)metode mengembalikan a bool- truejika item ditambahkan ke koleksi; falsejika item tersebut sudah ada.

Donat
sumber
9
Item T dalam hal ini harus mengimplementasikan antarmuka IEquatable. Jika kelas tidak mewarisi antarmuka ini, HashSet <T> menambahkan elemen duplikat.
Rudolf Dvoracek
Atau alih-alih mengimplementasikan item IEquatable, Anda bisa meneruskan implementasi (kustom) dari EqualityComparer<T>instance ke HashSet<T>konstruktor.
Sipke Schoorstra
17

Bagaimana dengan metode ekstensi di HashSet?

public static void AddOrThrow<T>(this HashSet<T> hash, T item)
{
    if (!hash.Add(item))
        throw new ValueExistingException();
}
Jonathon Reinhart
sumber
13

Dari HashSet<T>halaman di MSDN:

Kelas HashSet (Of T) menyediakan operasi set berkinerja tinggi. Satu set adalah kumpulan yang tidak berisi elemen duplikat , dan yang elemennya tidak berada dalam urutan tertentu.

(penekanan saya)

Oded
sumber
4

Jika yang Anda butuhkan hanyalah memastikan keunikan elemen, maka HashSet adalah yang Anda butuhkan.

Apa yang Anda maksud ketika Anda mengatakan "hanya implementasi set"? Satu set adalah (menurut definisi) kumpulan elemen unik yang tidak menyimpan urutan elemen.

Lloyd
sumber
Anda sepenuhnya benar; pertanyaannya agak bodoh. Pada dasarnya, saya mencari sesuatu yang akan memunculkan pengecualian ketika duplikat ditambahkan (seperti Dictionary <TKey, TValue>), tetapi seperti yang telah disebutkan, HashSet <T> mengembalikan false pada duplikat tambahan. +1, terima kasih.
Adam Rackis
3

Hanya untuk menambahkan 2 sen saya ...

jika Anda membutuhkan ValueExistingException-throwing, HashSet<T>Anda juga dapat membuat koleksi Anda dengan mudah:

public class ThrowingHashSet<T> : ICollection<T>
{
    private HashSet<T> innerHash = new HashSet<T>();

    public void Add(T item)
    {
        if (!innerHash.Add(item))
            throw new ValueExistingException();
    }

    public void Clear()
    {
        innerHash.Clear();
    }

    public bool Contains(T item)
    {
        return innerHash.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        innerHash.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return innerHash.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        return innerHash.Remove(item);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return innerHash.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

ini dapat berguna misalnya jika Anda membutuhkannya di banyak tempat ...

digEmAll
sumber
Tentu. Saya ingin tahu apakah ada yang sudah terpasang, tapi terima kasih +1
Adam Rackis
0

Anda dapat melihat semacam Daftar Unik sebagai berikut

public class UniqueList<T>
{
    public List<T> List
    {
        get;
        private set;
    }
    List<T> _internalList;

    public static UniqueList<T> NewList
    {
        get
        {
            return new UniqueList<T>();
        }
    }

    private UniqueList()
    {            
        _internalList = new List<T>();
        List = new List<T>();
    }

    public void Add(T value)
    {
        List.Clear();
        _internalList.Add(value);
        List.AddRange(_internalList.Distinct());
        //return List;
    }

    public void Add(params T[] values)
    {
        List.Clear();
        _internalList.AddRange(values);
        List.AddRange(_internalList.Distinct());
       // return List;
    }

    public bool Has(T value)
    {
        return List.Contains(value);
    }
}

dan Anda bisa menggunakannya seperti berikut

var uniquelist = UniqueList<string>.NewList;
uniquelist.Add("abc","def","ghi","jkl","mno");
uniquelist.Add("abc","jkl");
var _myList = uniquelist.List;

hanya akan "abc","def","ghi","jkl","mno"selalu kembali meskipun duplikat ditambahkan ke dalamnya

Vinod Srivastav
sumber