热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

为什么HashSet<Point>比HashSet<string>慢得多?

如何解决《为什么HashSet<Point>比HashSet<string>慢得多?》经验,为你挑选了2个好方法。

我想存储一些像素位置而不允许重复,所以首先想到的是HashSet或类似的类.然而,与类似的情况相比,这似乎非常缓慢HashSet.

例如,这段代码:

HashSet points = new HashSet();
using (Bitmap img = new Bitmap(1000, 1000))
{
    for (int x = 0; x 

大约需要22.5秒.

虽然以下代码(由于显而易见的原因不是一个好的选择)只需1.6秒:

HashSet points = new HashSet();
using (Bitmap img = new Bitmap(1000, 1000))
{
    for (int x = 0; x 

所以,我的问题是:

这有什么理由吗?我检查了这个答案,但22.5秒比答案中显示的数字更多.

有没有更好的方法来存储没有重复的点?

Hans Passant.. 286

Point结构引发了两个性能问题.添加Console.WriteLine(GC.CollectionCount(0));到测试代码时可以看到的内容.您会看到Point测试需要~3720个集合,但字符串测试只需要~18个集合.不是免费的.当你看到一个值类型诱导如此多的集合时,你需要得出结论"呃哦,太多拳击".

问题是HashSet需要一个IEqualityComparer完成它的工作.由于你没有提供一个,它需要回退到一个返回的EqualityComparer.Default().该方法可以很好地完成字符串,它实现了IEquatable.但不是Point,它是一种类似于.NET 1.0的类型,从来没有得到泛型的爱.它所能做的只是使用Object方法.

另一个问题是Point.GetHashCode()在这个测试中没有做太多的工作,碰撞太多,所以它对Object.Equals()非常重要.String具有出色的GetHashCode实现.

您可以通过为HashSet提供一个好的比较器来解决这两个问题.像这个:

class PointComparer : IEqualityComparer {
    public bool Equals(Point x, Point y) {
        return x.X == y.X && x.Y == y.Y;
    }

    public int GetHashCode(Point obj) {
        // Perfect hash for practical bitmaps, their width/height is never >= 65536
        return (obj.Y <<16) ^ obj.X;
    }
}

并使用它:

HashSet list = new HashSet(new PointComparer());

它现在大约快150倍,轻松击败字符串测试.



1> Hans Passant..:

Point结构引发了两个性能问题.添加Console.WriteLine(GC.CollectionCount(0));到测试代码时可以看到的内容.您会看到Point测试需要~3720个集合,但字符串测试只需要~18个集合.不是免费的.当你看到一个值类型诱导如此多的集合时,你需要得出结论"呃哦,太多拳击".

问题是HashSet需要一个IEqualityComparer完成它的工作.由于你没有提供一个,它需要回退到一个返回的EqualityComparer.Default().该方法可以很好地完成字符串,它实现了IEquatable.但不是Point,它是一种类似于.NET 1.0的类型,从来没有得到泛型的爱.它所能做的只是使用Object方法.

另一个问题是Point.GetHashCode()在这个测试中没有做太多的工作,碰撞太多,所以它对Object.Equals()非常重要.String具有出色的GetHashCode实现.

您可以通过为HashSet提供一个好的比较器来解决这两个问题.像这个:

class PointComparer : IEqualityComparer {
    public bool Equals(Point x, Point y) {
        return x.X == y.X && x.Y == y.Y;
    }

    public int GetHashCode(Point obj) {
        // Perfect hash for practical bitmaps, their width/height is never >= 65536
        return (obj.Y <<16) ^ obj.X;
    }
}

并使用它:

HashSet list = new HashSet(new PointComparer());

它现在大约快150倍,轻松击败字符串测试.


它的灵感来自鼠标在窗户中的位置.对于您想要显示的任何位图,它都是完美的哈希.
+1用于提供GetHashCode方法实现.只是出于好奇,你是怎么来特别的`obj.X <<16 | obj.Y;`实施.
@AkashKC我不是很熟悉C#,但据我所知,整数通常是32位.在这种情况下,您需要2个数字的哈希值,并通过左移一个16位,确保每个数字的"低"16位不会用"|""影响"另一个数字.对于3个数字,将22和11用作移位是有意义的.对于4个数字,它将是24,16,8.但是仍然会发生碰撞但只有数字变大.但它也至关重要取决于`HashSet`的实现.如果它使用带有"位截断"的开放式地址(我不认为它!),左移方法可能会很糟糕.
@HansPassant:我想知道在GetHashCode中使用XOR而不是OR可能稍好一些 - 如果点坐标可能超过16位(可能不在常见显示器上,但不久将来).// XOR在散列函数中通常比OR好,因为它丢失的信息较少,反转等等.//例如,如果允许负坐标,考虑如果Y为负,X贡献会发生什么.
很高兴知道.编写像你这样的哈希码的任何文档或最佳指南?实际上,我仍然想知道上面的哈希码是否伴随您的经验或您遵循的任何准则.

2> InBetween..:

性能下降的主要原因是拳击正在进行(正如Hans Passant的回答中所述).

除此之外,哈希码算法使问题恶化,因为它会导致更多的调用,Equals(object obj)从而增加装箱转换的数量.

另请注意,哈希码的Point计算方法是x ^ y.这会在您的数据范围内产生非常小的色散,因此HashSet人口过多 - 这种情况不会发生string,其中散列的散射要大得多.

您可以通过实现自己的Point结构(平凡)并使用更好的哈希算法来解决该问题,例如通过移动坐标:

(x <<16) ^ y

有关哈希码的一些好建议,请阅读Eric Lippert关于该主题的博客文章.


当你可以创建一个实现`IEqualityComparer `的类并且保持与`Point`一起使用的其他东西的兼容性同时获得没有穷人`GetHashCode`的好处并且需要时,不需要实现`Point`. `Equals()`中的框.
查看Point的参考源,`GetHashCode`执行:`unchecked(x ^ y)`而对于`string`,它看起来要复杂得多.
@AhmedAbdelhameed这可能是因为你在哈希集中添加的成员比你意识到的少(再次由于哈希码算法的可怕分散).完成填充后,"list"的数量是多少?
@AhmedAbdelhameed你的测试是错误的.你是一遍又一遍地添加相同的长片,所以实际上你插入的元素很少.插入`point`时,`HashSet`将在内部调用`GetHashCode`,对于每个具有相同哈希码的点,将调用`Equals`来确定它是否已经存在
嗯..好吧,为了检查你的假设是否正确,我只是尝试使用`HashSet ()`,并使用`list.Add(unchecked(x ^ y));`来向HashSet添加值.这实际上甚至比`HashSet `*(345 ms)*更快.这与你描述的有什么不同吗?
@AhmedAbdelhameed - 不知道,但我看了[这里](https://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Point.cs,a041be61667d4c9a​​)
@InBetween我不打算使用`Point`,除非我需要它用于点东西,在这种情况下我需要`Point`.在正面,corefx有一个更好的哈希代码和一个`IEquatable `的实现,希望有一天会转移到netfx.
推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 本文介绍了在wepy中运用小顺序页面受权的计划,包含了用户点击作废后的从新受权计划。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文记录了在vue cli 3.x中移除console的一些采坑经验,通过使用uglifyjs-webpack-plugin插件,在vue.config.js中进行相关配置,包括设置minimizer、UglifyJsPlugin和compress等参数,最终成功移除了console。同时,还包括了一些可能出现的报错情况和解决方法。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • EPPlus绘制刻度线的方法及示例代码
    本文介绍了使用EPPlus绘制刻度线的方法,并提供了示例代码。通过ExcelPackage类和List对象,可以实现在Excel中绘制刻度线的功能。具体的方法和示例代码在文章中进行了详细的介绍和演示。 ... [详细]
author-avatar
丽春院少爷
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有