在开发的过程中常常会碰到一种需求,两个集合融合到一个集合里面,同时去除相同的成员。
说到这里,可能很多人脑中立刻就闪现了Union,Distinct之类的函数。的确,这两个函数可以解决大部分的问题。根据处理的对象的类型不同,实现需求的代码和原理都不一样。
1. 对象是系统自带的值类型。可以直接调用Union,即可实现上面的需求。
var x = new List<int> { 1, 2, 3, 4, 5, 6 }; var y = new List<int> { 6, 7, 8, 9, 10 }; var res = x.Union(y).ToList();//结果为{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
2. 对象是系统自带的引用类型。由于对象为引用类型,在没有特殊处理的前提下,实际执行Union操作时,进行相同比较时的参照为对象的引用地址,表面上看起来是一样的,但是后台的引用地址不同,比较是否相同时就会返回‘不一样’。这种情况下,需要自定义一个实现了IEqualityComparer<T>的类作为参数传递进去。
var x = new List<List<int>> { new List<int>() {1}, new List<int>() {2}, new List<int>() {3} }; var y = new List<List<int>> { new List<int>() {3}, new List<int>() {4}, new List<int>() {5} }; var res = x.Union(y).ToList();//结果Count为6
查看Union的函数,它有一个重载,Union(IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)。也就是说我们可以自定义相同比较的规则。修改代码如下:
public Form2() { InitializeComponent(); var x = new List<List<int>> {new List<int>() {1}, new List<int>() {2}, new List<int>() {3}}; var y = new List<List<int>> {new List<int>() {3}, new List<int>() {4}, new List<int>() {5}}; var res = x.Union(y, new ListComparer<int>()).ToList();//结果Count为5 } class ListComparer<T> : IEqualityComparer<List<T>> { public bool Equals(List<T> x, List<T> y) { var except = x.Except(y); return except.Count() == 0 && x.Count == y.Count; } public int GetHashCode(List<T> obj) { return obj.Select(x => x.GetHashCode()).Aggregate((x, y) => x ^ y); } }
3. 对象是自定义的值类型。自定义的值类型需要在定义过程中按照自己的需要重写Equals的方法。如果不重写也可以,在执行Union函数时,会调用系统的ValueType.Equals方法。只是这样就不一定是我们想要的结果了。贴上代码一目了然。在下面的TestB中,我要求只要ID相同,实例就是相同的。根据这个规则,最后的res.Count为4.
public Form2() { InitializeComponent(); var x = new List<TestB> {new TestB("One", "1"), new TestB("Two", "2"), new TestB("Three", "3")}; var y = new List<TestB> {new TestB("One", "4"), new TestB("Two", "5"), new TestB("Six", "6")}; var res = x.Union(y).ToList(); } struct TestB { public string ID; public string Value; public TestB(string id, string value) { this.ID = id; this.Value = value; } public override string ToString() { return string.Format("{0}_{1}", ID, Value); } public override bool Equals(object obj) { if (obj is TestB) return this.ID.Equals(((TestB)obj).ID); else return false; } }
4. 对象是自定义的引用类型。综合以上三种情况的分析和实现方法,不难得出这种情况下有两种实现方法。
方法一:直接在自定义的过程中,重写GetHashCode和Equals函数;
public Form2() { InitializeComponent(); var x = new List<TestA> {new TestA("One", "1"), new TestA("Two", "2"), new TestA("Three", "3")}; var y = new List<TestA> {new TestA("One", "1"), new TestA("Two", "5"), new TestA("Six", "6")}; var res = x.Union(y).ToList(); } class TestA { public string ID; public string Value; public TestA(string id, string value) { this.ID = id; this.Value = value; } public override string ToString() { return string.Format("{0}_{1}", ID, Value); } public override bool Equals(object obj) { if (obj is TestA) return this.GetHashCode().Equals(((TestA)obj).GetHashCode()); else return false; } public override int GetHashCode() { int hashID = ID.GetHashCode(); int hashValue = Value.GetHashCode(); return hashID ^ hashValue; } }
方法二:另外定义一个类,实现IEqualityComparer<T>,作为相同的判断规则传入到Union函数中。
public Form2() { InitializeComponent(); var x = new List<TestA> { new TestA("One", "1"), new TestA("Two", "2"), new TestA("Three", "3") }; var y = new List<TestA> { new TestA("One", "1"), new TestA("Two", "5"), new TestA("Six", "6") }; var res = x.Union(y, new TestAComparer()).ToList(); } class TestA { public string ID; public string Value; public TestA(string id, string value) { this.ID = id; this.Value = value; } public override string ToString() { return string.Format("{0}_{1}", ID, Value); } } class TestAComparer : IEqualityComparer<TestA> { public bool Equals(TestA x, TestA y) { return GetHashCode(x).Equals(GetHashCode(y)); } public int GetHashCode(TestA obj) { int hashID = obj.ID.GetHashCode(); int hashValue = obj.Value.GetHashCode(); return hashID ^ hashValue; } }
留文备用。转载请注明出处:http://www.cnblogs.com/icyJ/