C#中Linq的去重方式Distinct詳解
前天在做批量數(shù)據(jù)導(dǎo)入新增時(shí),要對(duì)數(shù)據(jù)進(jìn)行有效性判斷,其中還要去除重復(fù),如果沒出現(xiàn)linq的話可能會(huì)新聲明一個(gè)臨時(shí)對(duì)象集合,然后遍歷原始數(shù)據(jù)判斷把符合條件的數(shù)據(jù)添加到臨時(shí)集合中,這在有了linq之后顯得比較麻煩。
一、首先創(chuàng)建一個(gè)控制臺(tái)應(yīng)用程序,添加一個(gè)Person對(duì)象
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Compare { public class Person { public string Name { get; set; } public int Age { get; set; } public Person(string name, int age) { this.Name = name; this.Age = age; } } }
二、創(chuàng)建測試數(shù)據(jù)
創(chuàng)建了一個(gè)Name="ZhangSan"的Person對(duì)象,放入personList兩次,然后personList又創(chuàng)建了幾個(gè)Person對(duì)象,這幾個(gè)Person對(duì)象中也有Name、Age都重復(fù)的。例如:"XiaoMing",26.
Person person = new Person("ZhangSan",26); List<Person> personList = new List<Person>() { person, new Person("XiaoMing",25), new Person("CuiYanWei",25), new Person("XiaoMing",26), new Person("XiaoMing",25), new Person("LaoWang",26), new Person("XiaoMing",26), person };
三、測試
下面的代碼中用了兩種方式來選擇不重復(fù)的數(shù)據(jù)。
List<Person> defaultDistinctPersons = personList.Distinct().ToList<Person>(); foreach (Person p in defaultDistinctPersons) { Console.WriteLine("Name:{0} Age:{1}",p.Name,p.Age); } Console.WriteLine("-----------------------------------------------------"); List<Person> comparePersons = personList.Distinct(new PersonCompare()).ToList<Person>(); foreach (Person p in comparePersons) { Console.WriteLine("Name:{0} Age:{1}", p.Name, p.Age); } Console.ReadLine();
在華麗分割線上面是使用默認(rèn)的distinct,下面是通過集成IEqualityComparer接口。下面是實(shí)現(xiàn)接口的代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Compare { public class PersonCompare:IEqualityComparer<Person> { public bool Equals(Person x, Person y) { if (x == null || y == null) return false; return x.Name.Equals(y.Name) && x.Age == y.Age; } public int GetHashCode(Person obj) { return obj.GetHashCode(); } } }
在上面的代碼中,繼承IEqualityComparer接口,主要是實(shí)現(xiàn)了兩個(gè)方法:bool Equals(T x, T y);int GetHashCode(T obj);可能即使實(shí)現(xiàn)了接口也不了解里面是怎么個(gè)原理,我們先看下運(yùn)行結(jié)果。
從上面的運(yùn)行結(jié)果可以看到,兩個(gè)運(yùn)行結(jié)果是一樣的,還是有重復(fù)的數(shù)據(jù):例如XiaoMing,26.兩個(gè)都沒去除重復(fù),只有ZhangSan那兩個(gè)去除重復(fù)了。是不是有實(shí)現(xiàn)接口多此一舉的感覺。那為什么還要有這個(gè)接口還要實(shí)現(xiàn)它呢?其實(shí)要說下GetHashCode和Equals。
在說GetHashCode和Equals之前先了解下distinct(),這個(gè)方法Distinct 默認(rèn)比較的是對(duì)象的引用,所以使用默認(rèn)的distinct()方法是ZhangSan對(duì)象是過濾除去的,而XiaoMing,26是兩個(gè)不同的對(duì)象,沒有除去。
然后說下GetHashCode和Equals兩個(gè)方法.
1.哈希碼哈希代碼是一個(gè)用于在相等測試過程中標(biāo)識(shí)對(duì)象的數(shù)值。它還可以作為一個(gè)集合中的對(duì)象的索引。如果兩個(gè)對(duì)象的 Equals 比較結(jié)果相等,則每個(gè)對(duì)象的 GetHashCode 方法都必須返回同一個(gè)值。 如果兩個(gè)對(duì)象的比較結(jié)果不相等,這兩個(gè)對(duì)象的 GetHashCode 方法不一定返回不同的值.
簡而言之,如果你發(fā)現(xiàn)兩個(gè)對(duì)象 GetHashCode() 的返回值相等,那么這兩個(gè)對(duì)象就很可能是同一個(gè)對(duì)象;但如果返回值不相等,這兩個(gè)對(duì)象一定不是同一個(gè)對(duì)象.
當(dāng)GetHashCode可以直接分辨出不相等時(shí),Equals就沒必要調(diào)用了,而當(dāng)GetHashCode返回相同結(jié)果時(shí),Equals方法會(huì)被調(diào)用從而確保判斷對(duì)象是否真的相等。所以,還是那句話:GetHashCode沒必要一定把對(duì)象分辨得很清楚(況且它也不可能,一個(gè)int不可能代表所有的可能出現(xiàn)的值),有Equals在后面做保障。GetHashCode僅需要對(duì)對(duì)象進(jìn)行快速判斷。
上面的幾句算是總結(jié)性的說明了兩個(gè)方法的是怎么個(gè)路子,這也能解釋出ZhangSan的重復(fù)去除,而其他的幾個(gè)對(duì)象沒有去重復(fù)的原因,ZhangSan那是一個(gè)對(duì)象,其他的雖然Name、Age相等,但不是同一個(gè)對(duì)象。
我們可以稍微改動(dòng)下代碼來驗(yàn)證上面的語句.在實(shí)現(xiàn)IEqualityComparer的接口類中打印出一些信息就能看明白
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Compare { public class PersonCompare:IEqualityComparer<Person> { public bool Equals(Person x, Person y) { if (x == null || y == null) return false; Console.WriteLine("XName:{0} XAge:{1} XHashCode:{2} YName:{3} YAge:{4} YHashCode:{5}", x.Name, x.Age, x.GetHashCode(),y.Name,y.Age,y.GetHashCode()); return x.Name.Equals(y.Name) && x.Age == y.Age; } public int GetHashCode(Person obj) { Console.WriteLine("GetHashCode Name:{0} Age:{1} HashCode:{2}",obj.Name,obj.Age,obj.GetHashCode()); return obj.GetHashCode(); } } }
在GetHashCode中打印了對(duì)象的Name、Age和HashCode??梢钥吹紿ashCode只有ZhangSan的是相同的,在Equals方法中只打印出了ZhangSan的,還是因?yàn)樯厦娴南扰袛郒ashCode,相等了再使用Equals判斷。
我們?cè)俑膭?dòng)下實(shí)現(xiàn)IEqualityComparer的接口類
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Compare { public class PersonCompare:IEqualityComparer<Person> { public bool Equals(Person x, Person y) { if (x == null || y == null) return false; Console.WriteLine("XName:{0} XAge:{1} XHashCode:{2} YName:{3} YAge:{4} YHashCode:{5}", x.Name, x.Age, x.GetHashCode(), y.Name, y.Age, y.GetHashCode()); return x.Name.Equals(y.Name) && x.Age == y.Age; } public int GetHashCode(Person obj) { //Console.WriteLine("GetHashCode Name:{0} Age:{1} HashCode:{2}",obj.Name,obj.Age,obj.GetHashCode()); //return obj.GetHashCode(); string s = string.Format("{0}_{1}",obj.Name,obj.Age); Console.WriteLine("Name:{0} Age:{1} HashCode:{2}",obj.Name,obj.Age, s.GetHashCode()); return s.GetHashCode(); } } }
根據(jù)上面的的代碼和測試結(jié)果我們可以看到,GetHashCode執(zhí)行了7次(7個(gè)對(duì)象),Equals執(zhí)行了3次,因?yàn)閆hangSan,26和XiaoMing,25兩個(gè)的哈希碼是一樣的就沒有繼續(xù)往下執(zhí)行。
到此這篇關(guān)于Linq之Distinct詳解的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Unity UGUI的LayoutElement布局元素組件介紹使用示例
這篇文章主要為大家介紹了Unity UGUI的LayoutElement布局元素組件介紹使用示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07C#實(shí)現(xiàn)多個(gè)計(jì)時(shí)器記錄不同定時(shí)時(shí)間
這篇文章主要為大家詳細(xì)介紹了C#實(shí)現(xiàn)多個(gè)計(jì)時(shí)器記錄不同定時(shí)時(shí)間,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12詳細(xì)介紹C#之文件校驗(yàn)工具的開發(fā)及問題
目前校驗(yàn)文件使用最多的是MD值和SHA值,不外乎有些使用CRC,前段時(shí)間微軟發(fā)布了VisualStudio正式版,win鏡像,微軟官方給出的校驗(yàn)方式都是校驗(yàn)文件的SHA值。下面詳細(xì)介紹C#之文件校驗(yàn)工具的開發(fā)及問題,需要的朋友可以參考下2015-07-07