一种集合“相等性”的实现
最近在工作中遇到了一个小的功能,就是需要向一个服务发送请求命令,需要判断请求是否发生变化,如果发生变化了,则重新请求。该问题实际上就是判断两个集合是否相等,只需要记录最后一次请求的元素的集合,然后将其和最新一次进行比较是否相等。需要说明的是这里定义的集合相等是指:两个集合如果元素值一样并且出现的次数也一样,即使顺序不一样也认为是相等,比如集合A={1,2,3,4,4,5} 集合B={1,4,4,2,3,5} 这两个集合也认为是相等的。后面讨论的集合相等都是基于这一假设的。
就这么个简单的问题,也有不同种解决方法,这里和大家分享一下。
方法一 使用Dictionary计数来实现
这种方法思路很简单,创建一个Dictionary对象,将第一个集合中的元素作为key添加到Dictionary中,value即为出现的次数。然后遍历第二个集合,如果包含相同的key,则value减1,如果不好含,则直接返回false,表示两个集合不同。最后,如果Dictionary中所有key对应的value都为0即表示两个集合相等,否则不相等。
/// <summary>
/// 判断两个集合是否相等,相等 表示元素值及出现的次数一样即可,顺序可以不一样。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list1"></param>
/// <param name="list2"></param>
/// <returns></returns>
public static bool ScrambledEquals<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
//如果集合个数不相等,则集合不同
if (list1.Count() != list2.Count()) return false;
var cnt = new Dictionary<T, int>();
foreach (T s in list1)
{
if (cnt.ContainsKey(s))
{
cnt[s]++;
}
else
{
cnt.Add(s, 1);
}
}
foreach (T s in list2)
{
if (cnt.ContainsKey(s))
{
cnt[s]--;
}
else //如果第二个集合中有第一个集合中未包含的元素,表示两个集合不同
{
return false;
}
}
return cnt.Values.All(c => c == 0);
}
算法需要对象实现IEquatable接口,而该接口一般对象均默认实现。
以上算法的效率是很高的,时间复杂度为O(N),因为对Dictionary的查找时间复杂度为O(1),所以主要时间都花在遍历集合上。
以上方法对.NET不同的版本都兼容,如果是.NET 2.0版本也非常容易改造,只需要把最后一句代码改为遍历即可。由于项目原因,本人开发环境为2.0所以才用的是该方法。
方法二 使用IEnumerable的SequenceEqual 扩展方法
在.NET 3.5 中,比较集合元素的相等性有了新的方法,IEnumerable接口提供了名为SequenceEqual的方法,该方法用于判断两个序列是否顺序相等,即两个源序列的长度相等,且其相应元素相等。
我们可以先对两个集合进行排序,然后直接调用Enumerable.SequenceEqual 方法即可,这大概是最简单的实现方法了。
public static bool ScrambledEqualsUsingSequenceEqual<T>(IEnumerable<T> list1, IEnumerable<T> list2)
{
return Enumerable.SequenceEqual(list1.OrderBy(t => t), list2.OrderBy(t => t));
}
需要注意的是,如果要自定义相等特性,需要实现IEquatable<T>接口,并提供自定义的Equal和GetHashCode实现。
方法三 使用CollectionAssert.AreEquivalent方法
在Visual Studio的单元测试框架中,位于Microsoft.VisualStudio.TestTools.UnitTesting命名空间下,以及在NUnit中都存在有CollectionAssert.AreEquivalent方法,该方法的解释是:
“Two collections are equivalent if they have the same elements in the same quantity, but in any order. Elements are equal if their values are equal, not if they refer to the same object.”
从定义可以看出 这正是我们定义的集合相等性。所以可以直接使用该方法。需要注意的是该命名空间位于Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll这个dll中。

使用Reflector工具,可以查看其具体实现,对其代码进行简单修改可以看到其原理如下:
public class MultiSetComparer<T> : IEqualityComparer<IEnumerable<T>>
{
public bool Equals(IEnumerable<T> first, IEnumerable<T> second)
{
if (first == null)
return second == null;
if (second == null)
return false;
if (ReferenceEquals(first, second))
return true;
var firstCollection = first as ICollection<T>;
var secondCollection = second as ICollection<T>;
if (firstCollection != null && secondCollection != null)
{
if (firstCollection.Count != secondCollection.Count)
return false;
if (firstCollection.Count == 0)
return true;
}
return !HaveMismatchedElement(first, second);
}
private static bool HaveMismatchedElement(IEnumerable<T> first, IEnumerable<T> second)
{
int firstCount;
int secondCount;
var firstElementCounts = GetElementCounts(first, out firstCount);
var secondElementCounts = GetElementCounts(second, out secondCount);
if (firstCount != secondCount)
return true;
foreach (var kvp in firstElementCounts)
{
firstCount = kvp.Value;
secondElementCounts.TryGetValue(kvp.Key, out secondCount);
if (firstCount != secondCount)
return true;
}
return false;
}
private static Dictionary<T, int> GetElementCounts(IEnumerable<T> enumerable, out int nullCount)
{
var dictionary = new Dictionary<T, int>();
nullCount = 0;
foreach (T element in enumerable)
{
if (element == null)
{
nullCount++;
}
else
{
int num;
dictionary.TryGetValue(element, out num);
num++;
dictionary[element] = num;
}
}
return dictionary;
}
public int GetHashCode(IEnumerable<T> enumerable)
{
int hash = 17;
foreach (T val in enumerable.OrderBy(x => x))
hash = hash * 23 + val.GetHashCode();
return hash;
}
}
其实现方法原理和第一个方法类似,首先是进行了一系列条件判断,是否为空,元素个数是否相等等等。然后也是创建一个Dictionary进行元素个数计算,并比较。
一种集合“相等性”的实现的更多相关文章
- JAVA:三种集合LIST、SET、MAP
1. 集合框架介绍 我 们知道,计算机的优势在于处理大量的数据,在编程开发中,为处理大量的数据,必须具备相应的存储结构,数组可以用来存储并处理大量类型相同的数 据,但是会发现数组在应用中的限制:数组长 ...
- Windows Phone中的几种集合控件
前言 Windows Phone开发过程中不可避免的就是和集合数据打交道,如果之前做过WP App的开发的话,相信你已经看过了各种集合控件的使用.扩展和自定义.这些个内容在这篇博客里都没有,那么我们今 ...
- Spring学习(三)几种集合属性的注入方式
1.前言 众所周知.java中不只有八大简单类型.还有一些集合类型.本文围绕集合类型的注入做一个总结. 2.项目骨架 3.过程 1.创建实体类AllCollectionType package com ...
- Redis中7种集合类型应用场景&redis常用命令
Redis常用数据类型 Redis最为常用的数据类型主要有以下五种: String Hash List Set Sorted set 在具体描述这几种数据类型之前,我们先通过一张图了解下Redis内部 ...
- OC中几种集合的遍历方法(数组遍历,字典遍历,集合遍历)
// 先分别初始化数组.字典和集合,然后分别用for循环.NSEnumerator枚举器和forin循环这三个方法来实现遍历 NSArray *array = @[@"yinhao" ...
- Redis中7种集合类型应用场景
StringsStrings 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字.使用Strings类型,你可以完全实现目前 Memcached 的功能,并且效率更 ...
- Java测试开发--Set、Map、List三种集合(四)
1.集合类型主要有3种:set(集).list(列表)和map(映射). 2.三者关系 3.Set set接口是Collection接口的一个子接口,是无序的,set去重,也就是说set中不存在两个这 ...
- Java常用的几种集合, Map集合,Set集合,List集合
Java中 Object是所有类的根 Java集合常用的集合List集合.Set集合.Map集合 Map接口常用的一些方法 size() 获取集合中名值对的数量 put(key k, value v ...
- Java基础知识强化之集合框架笔记73:如何选择使用哪种集合
1. 到底使用那种集合. 看需求 是否是键值对象形式: 是:Map 键是否需要排序: 是:TreeMap 否:HashMap 不知道,就使用HashMap. 否:Collection 元素是否唯 ...
随机推荐
- 来自MarsEdit的博客测试
使用MarsEdit编辑的第一个测试博客. 希望我们一帆风顺! 插图,在插图时可以调整尺寸: 六种公式写法,记得要在选项中打开-启用数学公式: \begin{equation}\sum\end{e ...
- ServletContext获取的方法
ServletContext 代表当前web应用 如何获取ServletContext对象 ServletConfig对象中维护了ServletContext对象的引用,可以通过以下方式获得 Ser ...
- node学习笔记(三)
//事件驱动events //events是node最重要的模块没有之一,因为node.js本身的架构就是事件式的,而他提供了唯一的接口,所以堪称node.js事件编程的基石; //events几乎被 ...
- 简单jquery实现select三级联动
简单的jquery实现select三级联动 代码如下: <!DOCTYPE html> <html> <head> <meta charset="u ...
- UVa 11292 Dragon of Loowater
简单贪心 龙头的直径和人的佣金排序,价值小的人和直径小的配 #include<iostream> #include<cstdio> #include<cmath> ...
- (转)Java中的static关键字解析
转自http://www.cnblogs.com/dolphin0520/p/3799052.html 一.static关键字的用途 在<Java编程思想>P86页有这样一段话: “sta ...
- UVALive 2191 Potentiometers (树状数组)
题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...
- 创建GitHub技术博客全攻略
http://blog.csdn.net/renfufei/article/details/37725057 http://www.pchou.info/web-build/2014/07/04/bu ...
- 表格里使用text-overflow后不能隐藏超出的文本的解决方法
当把text-overflow设为ellipsis时文本溢出内容就能显示为省略标记,而设为clip时就能把文本溢出的部分裁切掉,不过在表格里面使用text-overflow后依旧不能隐藏超 ...
- 关于Certificate、Provisioning Profile、App ID的介绍及其之间的关系
1.概念介绍 如果你拥有一个开发者账户的话,在iOS Dev Center打开Certificates, Indentifiers & Profiles,你就可以看到如下的列表: Profil ...