惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
Help Net Security
Help Net Security
A
About on SuperTechFans
博客园_首页
M
MIT News - Artificial intelligence
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
量子位
F
Fortinet All Blogs
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
爱范儿
爱范儿
L
LINUX DO - 最新话题
The GitHub Blog
The GitHub Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
C
Cybersecurity and Infrastructure Security Agency CISA
N
News and Events Feed by Topic
S
Security @ Cisco Blogs
A
Arctic Wolf
V
Vulnerabilities – Threatpost
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Tailwind CSS Blog
The Last Watchdog
The Last Watchdog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
H
Heimdal Security Blog
H
Hacker News: Front Page
Simon Willison's Weblog
Simon Willison's Weblog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Recent Announcements
Recent Announcements
N
Netflix TechBlog - Medium
SecWiki News
SecWiki News
酷 壳 – CoolShell
酷 壳 – CoolShell
I
InfoQ
MyScale Blog
MyScale Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
Vercel News
Vercel News
AI
AI
L
LINUX DO - 热门话题
G
Google Developers Blog
P
Proofpoint News Feed
腾讯CDC
Schneier on Security
Schneier on Security
美团技术团队
V
V2EX
PCI Perspectives
PCI Perspectives
L
Lohrmann on Cybersecurity
Jina AI
Jina AI
博客园 - 司徒正美

博客园 - Anders06

A memory leak issue with WPF Command Binding 我也谈面试 附赠一份题目 标注随测量物体旋转问题 How to detect memory leak issue 图纸中角度标注算法 认真的程序员最可爱 Clean Code反案例:什么是不好的API 避免陷阱,重写Equals方法您需要注意的其中2个原则 What’s the problems with the following code 对象池 关于设计和设计文档的2个补充 SelectMany ConverAll & Exists SOS:利用WPF RichTextBox 提取RTF, 莫名丢掉了Underline样式信息 想探讨两个关于设计文档问题 Don‘t Cry for Me, Argentina 拒绝高姿态 Please avoid implement C# Destruction SOS: How to popup a HwndSource on topmost
败给了IEqualityComparer
Anders06 · 2010-11-22 · via 博客园 - Anders06

LINQ中的Distinct方法能够帮助我们轻松地剔除集合里面相同的元素。 它提供了2个重载函数,其中一个允许我们传入IEqualityComparer<T> 接口, 给我们充分的自由来决定2个元素是否相同。

为了实现剔除一堆string集合中相同的string,我写下了如下的代码。 注:不区分大小写

source.Distinct(new StringComparer());

internal class StringComparer : IEqualityComparer<string>
{
    public bool Equals(string x, string y)
    {
        return string.Compare(x, y, true) == 0;
    }

    public int GetHashCode(string obj)
    {
        return obj.GetHashCode();
    }
}

结果调试的时候始终不对,结果始终不能剔除那些只是大小写有区别的字符串,如"good” 跟"Good”.

当时因为时间紧张,就先提交了,事后发现原来还是自己不严谨,败给了GetHashCode()方法。

印象中自己没怎么实现过IEqualityComparer, 到实现过多次的IComparer接口。在上面的例子中虽然我意识到在比较的时候忽略大小写,但是再实现GetHashCode(), 随手地用了上面的方式。

问题就在这,既然我们认为"good” 跟"Good”是一样的,那么我们必须给他俩返回一样的hash code,但显然以我上面实现方式它俩返回的hash code是不一样的。

在鄙人的《避免陷阱,重现Equals方法您需要注意的其中2个原则》与《Just Reflect 》 中,其实我是多次提到了这个问题,想不到这次还是马失前蹄了,罪过啊。

所以正确的写法应该是:

public int GetHashCode(string obj)
    {
        return obj.ToUpper().GetHashCode();
    }


扩充话题: 既然第一次写法返回的结果不正确,这就能说明Distinct扩展方法是先调用GetHashCode(), 再调用Equals(). 因为如果是先调用Equals()方面,理论上"good” 跟"Good” 是能剔除一个的。 问题是为什么要先调用GetHashCode(), 或则换种说法是为什么Distinct方法接受IEqualityComparer接口, 而不是IComparer接口。 因为判定2个对象是否相同,只需调用Equals() 方法就好。或者说因为 ICompare接口返回的是int,而不是直接的bool类型吗?那为啥不是IEquatable呢?

调试Framework的源代码,可以发现如下踪迹:

static IEnumerable<TSource> DistinctIterator<TSource>(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer) {
    Set<TSource> set = new Set<TSource>(comparer);
    foreach (TSource element in source)
        if (set.Add(element)) yield return element;
}

也就是说Distinct执行时在遍历集合的时候,是把那些非重复的元素放在了Set容器里,而非普通的List,这样的好处是因为Set是一个HashTable结构的容器类,可以提高执行效率。我们知道在HashTable里找一个元素的间复杂度为O(1). 而在List里查找的效率却是O(n)。

因此将Distinct方法接受IEqualityComparer接口,是很有必要的, 目前按照它的实现方式时间复杂度为O(n)。 而如果改为传IEquatable接口,那么时间复杂度就得变为O(n2).